UnityShader实例12:屏幕特效之马赛克(Mosaic)材质

2024-05-28 23:32

本文主要是介绍UnityShader实例12:屏幕特效之马赛克(Mosaic)材质,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

马赛克(Mosaic)材质



概述


马赛克(Mosaic),估计是大伙平时很常见最讨厌的图片处理手段,嘿嘿,没错我说的就是"打码"。好了,正经点,马赛克指现行广为使用的一种图像(视频)处理手段,此手段将影像特定区域的色阶细节劣化并造成色块打乱的效果,因为这种模糊看上去有一个个的小格子组成,便形象的称这种画面为马赛克。其目的通常是让图像大规模的降低图像()视频分辨率,而让图像(视频)的一些细节隐藏起来,使之无法辨认,一般用来保护隐私,或者隐藏某些不健康的画面。


原理



要实现马赛克的效果,需要把图片一个相当大小的正方形区域用同一个点的颜色来表示,相当于将连续的颜色离散化,因此我们可以想到用取整的方法来离散颜色,但是在我们的图片纹理坐标采样时在0到1的连续范围,因此我们需要将纹理坐标转换成实际大小的整数坐标,接下来要把图像这个坐标量化离散,比如对于一个分辨率为256X256像素的图片,马赛克块的大小为8X8像素,我们先得将纹理坐标乘以(256,256)使其映射到0到256的范围,相当于一个整数代表一个像素,再将纹理坐标取除以8之后取整,最后再将坐标乘以8,除以256.重新映射回0到1的范围,但是纹理坐标已经是离散的,而非连续的。


Shader代码实现




到代码实现部分了,如果被上面原理讲述绕晕的同学还是直接看代码吧(原谅我,理科生,作文从来没行过 )。首先还是得在属性里声明一个马赛克大小的参数 

 


Properties {_MainTex ("Base (RGB)", 2D) = "white" {}_MosaicSize("MosaicSize", int)=5
}


然后声明一个内置四元数变量_MainTex_TexelSize,这个变量的从字面意思是主贴图_MainTex的像素尺寸大小,是一个四元数,它的值为 Vector4(1 / width, 1 / height, width, height);这个属于unity的黑魔法,不知道是在哪里定义的,官方文档并没有解释,有知道的网友可以在回复里告诉我。


half4 _MainTex_TexelSize;



因为马赛克是针对像素操作,所以关键代码也在Frag函数里面实现:


float2 uv = (i.texcoord*_MainTex_TexelSize.zw) ;//将纹理坐标映射到分辨率的大小
uv = floor(uv/_MosaicSize)*_MosaicSize;//根据马赛克块大小进行取整
i.texcoord =uv*_MainTex_TexelSize.xy;//把坐标重新映射回0,1的范围之内
fixed4 col = tex2D(_MainTex, i.texcoord);


Shader完整代码



VF版本代码01
Shader "PengLu/Unlit/MosaicVF" {
Properties {_MainTex ("Base (RGB)", 2D) = "white" {}_MosaicSize("MosaicSize", int)=5
}SubShader {Tags { "RenderType"="Opaque" }LOD 100Pass {  CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata_t {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION;half2 texcoord : TEXCOORD0;};sampler2D _MainTex;float4 _MainTex_ST;half4 _MainTex_TexelSize;int _MosaicSize;v2f vert (appdata_t v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{float2 uv = (i.texcoord*_MainTex_TexelSize.zw) ;uv = floor(uv/_MosaicSize)*_MosaicSize;i.texcoord =uv*_MainTex_TexelSize.xy;fixed4 col = tex2D(_MainTex, i.texcoord);UNITY_OPAQUE_ALPHA(col.a);return col;}ENDCG}
}}

VF版本代码01效果:



C#脚本代码


要做成屏幕特效,还需要脚本配合,这里不做过多解释,脚本里重要部分有注释,需要注意的是几个函数,具体用处可以看官方文档(这里这里

OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)

Graphics.Blit(sourceTexture, destTexture,material)

Graphics.Blit(sourceTexture, destTexture);


完整C#脚本如下:


using UnityEngine;
using System.Collections;
using System;[ExecuteInEditMode]
[AddComponentMenu ("PengLu/ImageEffect/Mosaic")]
public class ImageEffect_Mosaic : MonoBehaviour {#region Variablespublic Shader MosaicShader = null;private Material MosaicMaterial = null;	public int MosaicSize = 8;#endregion//创建材质和shaderMaterial material{get{if(MosaicMaterial == null){MosaicMaterial = new Material(MosaicShader);MosaicMaterial.hideFlags = HideFlags.HideAndDontSave;	}return MosaicMaterial;}}// Use this for initializationvoid Start () {MosaicShader = Shader.Find("PengLu/Unlit/MosaicVF");// Disable if we don't support image effectsif (!SystemInfo.supportsImageEffects){enabled = false;return;}// Disable the image effect if the shader can't// run on the users graphics cardif (!MosaicShader || !MosaicShader.isSupported)enabled = false;return;}void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture){	if(MosaicSize > 0 && MosaicShader != null){material.SetInt("_MosaicSize", MosaicSize);//将马赛克尺寸传递给shaderGraphics.Blit(sourceTexture, destTexture,material);//将抓取的图像传递给gpu并用shader处理后,传回来}else{Graphics.Blit(sourceTexture, destTexture);}}// Update is called once per framevoid Update () {#if UNITY_EDITORif (Application.isPlaying!=true){MosaicShader = Shader.Find("PengLu/Unlit/MosaicVF");}#endif}public void OnDisable () {if (MosaicMaterial)DestroyImmediate (MosaicMaterial);}
}



马赛克效果变种



在网上查资料的过程中,也看到一些很有意思的马赛克算法变种,这里我只将它们的关键代码以及实现效果放上来,大家可以自己理解制作。

圆形马赛克


关键代码:

float2 intUV = (i.texcoord*_MainTex_TexelSize.zw) ;
float2 xyUV = floor(intUV/_MosaicSize)*_MosaicSize+0.5*_MosaicSize;
float disSample = length(xyUV-intUV);
float2 mosaicUV = xyUV*_MainTex_TexelSize.xy;
fixed4 col = tex2D(_MainTex, i.texcoord);
if(disSample<0.5*_MosaicSize)col = tex2D(_MainTex,mosaicUV);


代码效果:





正六边形(蜂巢)马赛克

算法原理参考这里这里,我稍微做了些简化,减少了些数学运算使之能支持tanget 2.0,算法不难理解,但是自己想出来却还是蛮难的,所以很佩服原作者,由此可见数学在shader编程中还是相当重要的;

关键代码:

const float TR = 0.866025f;//TR=√3
float2 xyUV = (i.texcoord*_MainTex_TexelSize.zw);
int wx = int (xyUV.x/1.5f/_MosaicSize);
int wy = int (xyUV.y/TR/_MosaicSize);float2 v1,v2;
float2 wxy =float2(wx,wy);
if(wx/2*2==wx){if(wy/2*2==wy){v1 = wxy;v2 = wxy+1;}else{v1 = wxy+float2(0,1);v2 = wxy+float2(1,0);}	
}
else{if(wy/2*2 == wy){v1 = wxy+float2(0,1);v2 = wxy+float2(1,0);}else{v1 = wxy;v2 = wxy+1;}
}
v1 *= float2(_MosaicSize*1.5f,_MosaicSize*TR);
v2 *= float2(_MosaicSize*1.5f,_MosaicSize*TR);float s1 = length(v1.xy-xyUV.xy);
float s2 = length(v2.xy-xyUV.xy);
fixed4 col = tex2D(_MainTex,v2*_MainTex_TexelSize.xy);
if(s1 < s2)  col = tex2D(_MainTex,v1*_MainTex_TexelSize.xy);  



代码效果:


这篇关于UnityShader实例12:屏幕特效之马赛克(Mosaic)材质的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1012000

相关文章

swiper实例

大家好,我是燐子,今天给大家带来swiper实例   微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件,常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法   以下是一个简单的 swiper 示例代码:   WXML(页面结构) <swiper autoplay="true" interval="3

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

笔记本电脑屏幕模糊?6招恢复屏幕清晰!

在数字化时代的浪潮中,笔记本电脑已成为我们生活、学习和工作中不可或缺的一部分。然而,当那曾经清晰明亮的屏幕逐渐变得模糊不清时,无疑给我们的使用体验蒙上了一层阴影。屏幕模糊不仅影响视觉舒适度,更可能对我们的工作效率和眼睛健康构成威胁。 遇到笔记本电脑屏幕模糊的情况时我们应该如何解决?本文将与大家分享6个简单易懂的解决方法。 方法一:调整Windows分辨率 电脑屏幕模糊显示不清晰怎

如何实现一台机器上运行多个MySQL实例?

在一台机器上一个MySQL服务器运行多个MySQL实例有什么好处?这里我先入为主给大家介绍这样做至少存在两个好处(看完这篇文章后理解会更透彻): (1)减轻服务器链接负担 (2)为不同的用户提供不同的mysqld服务器的访问权限以方便这些用户进行自我管理。   下面我介绍具体的实现过程: 一、准备工作     台式机一台、Windows系统、MySQL服务器(我安装的版本是MySQL

Docker Compose--安装Nginx--方法/实例

原文网址:Docker Compose--安装Nginx--方法/实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Docker Compose如何安装Nginx。 目录结构 ├── config│   ├── cert│   │   ├── xxx_bundle.pem│   │   └── xxx.key│   ├── conf.d│   └── nginx.co

用 idea 启动多个实例

在学习负载均衡的时候,要模拟多个实例均提供一个服务,我们要如何用 idea 启动多个实例呢?         如下图,我们已经启动了一个 ProductService 服务,现在想再启动两个相同的服务 1. 选中要启动的服务,右键选择 Copy Configuration... 2 在弹出的框中,选择 Modify options -> Add VM option

简单工厂模式--结合实例学习简单工厂模式

在讲解简单工厂模式之前,有必要先了解一下OO的一些原则  1.OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。也就是说,对于一个已有的软件,如果需要扩展,应当在不需修改      已有代码的基础上进行。   2.DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。简单点说

Struts2(一)---struts2的环境搭建及实例

刚刚接触struts2,有点懵懵懂懂,还是习惯于先写代码,然后慢慢来理解其中的思想。 这篇文章主要内容是strusts的环境搭建及通过一个简单的例子来理解到底是怎么使用struts来简化编程的。 1.项目结构如下如,包括必须的包 2.web.xml <?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="

PINN解偏微分方程实例4

PINN解偏微分方程实例4 一、正问题1. Diffusion equation2. Burgers’ equation3. Allen–Cahn equation4. Wave equation 二、反问题1. Burgers’ equation3. 部分代码示例   本文使用 PINN解偏微分方程实例1中展示的代码求解了以四个具体的偏微分方程,包括Diffusion,Burg

材质、纹理、贴图的关系

材质、纹理、贴图的关系 材质 (Material)包含贴图 (Map),贴图包含纹理 (Texture)。 纹理(Texture)是最基本的数据输入单位。