马赛克(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)材质相关推荐

  1. UnityShader实例17:屏幕特效之碎屏特效

    碎屏特效 概述        在前公司,由于工作项目的原因,需要在unity实现一个类似狂野飙车8 ,撞车翻车后的碎屏效果(如下图),从图可以看出,该特效除了碎屏的效果外还有个降低饱和度的操作,接下来 ...

  2. UnityShader实例15:屏幕特效之Bloom

    Bloom特效 概述        Bloom,又称"全屏泛光",是游戏中常用的一种镜头效果,是一种比较廉价的"伪HDR"效果(如下右图):使用了Bloom效果 ...

  3. UnityShader实例14:屏幕特效之高斯模糊(Gaussian Blur)

    高斯模糊(Gaussian Blur) 概述 高斯模糊(Gaussian Blur),也叫高斯平滑,在photoshop中也有高斯模糊滤镜,通常用它来减少图像噪声以及降低细节层次.从数学的角度来看,图 ...

  4. UnityShader实例08:溶解消融(Dissolve)材质

    溶解消融(Dissolve)材质 在不少3D游戏中,角色死亡后会有一个溶解消融的特效,这个除了粒子特效以外还需要shader的配合.下面就是本例实现的一个效果,当然没有例子配合看起来是搓搓的,不过效果 ...

  5. 【猫猫的Unity Shader之旅】之初识屏幕特效

    Shader除了可以处理模型的显示效果外,还有一个腻害的功能就是对要渲染的画面进行整体的处理,也就是屏幕特效.常见的比如<鬼泣4>里的动态模糊效果,景深效果,还有一些全局的颜色矫正,比如可 ...

  6. UnityShader屏幕特效之Bloom

    版权声明:本文为博主原创文章,如需转载请指明出处http://blog.csdn.net/u011047171 目录(?)[+] Bloom特效 概述        Bloom,又称"全屏泛 ...

  7. UnityShader - 屏幕特效 - 刺目光亮(Bloom)

    Bloom特效 概述 Bloom,又称"全屏泛光",是游戏中常用的一种镜头效果,是一种比较廉价的"伪HDR"效果(如下右图):使用了Bloom效果后,画面的对比 ...

  8. Shader攻占笔记(八)屏幕特效

    屏幕特效 前言 脚本基类 景深效果 脚本部分 着色器部分 碎屏效果 动态模糊 前言 [关于作业的狡辩] 本周内容与前几周相比,难度提高了一些,本章的shader需要配合相应的脚本使用.脚本基本上承担两 ...

  9. 《Unity着色器和屏幕特效》——2.2 进阶的透明效果

    本节书摘来自华章计算机<Unity着色器和屏幕特效>一书中的第2章,第2.2节,作者[美]杰米·迪恩(Jamie Dean),译 周翀,张薇,更多章节内容可以访问云栖社区"华章计 ...

最新文章

  1. 如何跨域取到response额外的的headers
  2. hibernate中的PO持久化对象及PO三种状态
  3. windows聚焦图片为什么不更新了_为什么头条粉丝数到几百,就涨不起来了,一直在辛苦更新...
  4. Codeforces Round #208 (Div. 2)D. Dima and Hares
  5. android 连接指定wifi
  6. 【java】Thread.Sleep 与 Thread.onSpinWait
  7. 4.28考试总结(下午)
  8. 08.Prevent exceptions from leaving destructors
  9. linux 下xhprof的安装和使用
  10. 配置终端服务和远程桌面服务器身份验证和加密级别
  11. java中的移位操作
  12. stm32F103 模拟I2C mpu6050收到数据全为0,或者地址为209,104,0x68,0xD0的一些解决办法总结
  13. 偶极子阵列天线(带反射板)+单层天线罩
  14. 一根不均匀的绳子,全部烧完需要1个小时,问怎样烧能计时1个小时15分钟
  15. 机器学习中的主动学习实现_我如何使用机器学习来帮助实现正念
  16. 以“掌上东航”为例,论混合开发在企业级项目中的实践
  17. win11 安卓子系统(WSA)安装教程
  18. 宇视摄像机实况画面不清晰排查方法
  19. (4)Artemis支持的协议
  20. Java学习笔记 算法 Algorithms Fourth Edition

热门文章

  1. 虚拟盘、逻辑盘、卷的区别
  2. 《少有人走的路》--斯科特.派克(美) 于海生译
  3. html5 精灵动画,H5案例分享:CreateJs-EaselJS精灵Sprite类和简明动画
  4. Django配置Bootstrap, js
  5. python文字转语音输出_Python 文本转语音
  6. 海量遥感数据处理与GEE云计算技术应用
  7. glyphicon 字体在 bootstrap4 中默认不支持了
  8. 明略数据及其董事长吴明辉分获2016中国大数据两大奖项
  9. python程序加密_三分钟教你python自动化加密Word
  10. 用Mouse_event()来控制鼠标操作