UnityShader实例12:屏幕特效之马赛克(Mosaic)材质
来源:互联网 发布:安卓手机数据恢复软件 编辑:程序博客网 时间:2024/05/02 04:56
马赛克(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 card if (!MosaicShader || !MosaicShader.isSupported) enabled = false; return;}void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture){if(MosaicSize > 0 && MosaicShader != null){ material.SetInt("_MosaicSize", MosaicSize);//将马赛克尺寸传递给shader Graphics.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=√3float2 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);
代码效果:
1 0
- UnityShader实例12:屏幕特效之马赛克(Mosaic)材质
- UnityShader实例15:屏幕特效之Bloom
- UnityShader实例17:屏幕特效之碎屏特效
- UnityShader屏幕特效之Bloom
- UnityShader实例13:屏幕特效之均值模糊(Box Blur)
- UnityShader实例14:屏幕特效之高斯模糊(Gaussian Blur)
- UnityShader实例16:屏幕特效之径向模糊(Radial Blur)
- UnityShader实例16:屏幕特效之径向模糊(Radial Blur)
- UnityShader实例05:Toon(卡通)材质
- UnityShader实例10:广告牌(Billboard)材质
- UnityShader实例10:广告牌(Billboard)材质
- UnityShader - 屏幕特效 - 刺目光亮(Bloom)
- UnityShader 屏幕特效 模糊
- Unityshader实例01:冰块材质
- Unityshader实例02:Xray材质
- UnityShader实例11:积雪材质
- UnityShader实例18:火焰材质
- UnityShader实例:遮挡透明材质
- 剑指Offer——在特殊数组中查找某数
- L6:TreeSet类、Comparator接口、Collections类
- 安卓去掉标题栏
- JAVA POI Excel导出,数据源可以是List<Map>或者List<Model>类型
- 有ID列的表
- UnityShader实例12:屏幕特效之马赛克(Mosaic)材质
- 火狐浏览器伪装微信内核自带浏览器的两种方法
- poj1001
- Introduction to Java Programming编程题3.28<判断两个长方形是否相交>
- eclipse集成SVN
- java中字符串函数split用法详解
- 报错:While checking alphas in/xxx/.png pngcrush caught libpng error
- 视图不能更新的检查,制约失效的检查
- 冒泡排序及其优化