UnityShader实例11:屏幕特效之马赛克(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 100

	Pass {
		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 Variables
	public Shader MosaicShader = null;
	private Material MosaicMaterial = null;
	public int MosaicSize = 8;

	#endregion

//创建材质和shader
	Material material
	{
		get
		{
			if(MosaicMaterial == null)
			{
				MosaicMaterial = new Material(MosaicShader);
				MosaicMaterial.hideFlags = HideFlags.HideAndDontSave;
			}
			return MosaicMaterial;
		}
	}

	// Use this for initialization
	void Start () {

		MosaicShader = Shader.Find("PengLu/Unlit/MosaicVF");

		// Disable if we don't support image effects
		if (!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 frame
	void Update () {
		#if UNITY_EDITOR
		if (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);

代码效果:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-07 18:23:49

UnityShader实例11:屏幕特效之马赛克(Mosaic)材质的相关文章

UnityShader实例15:屏幕特效之Bloom

http://blog.csdn.net/u011047171/article/details/48522073 Bloom特效 概述 Bloom,又称"全屏泛光",是游戏中常用的一种镜头效果,是一种比较廉价的"伪HDR"效果(如下右图):使用了Bloom效果后,画面的对比会得到增强,亮的地方曝光也会得到加强,画面也会呈现一种朦胧,梦幻的效果,婚纱摄影中照片处理经常用到这种类似处理效果.Bloom效果一般用来近似模拟HDR效果,效果也比较相向,但实现原理却完全不同.

UnityShader实例11:积雪材质

积雪材质 概述 积雪材质是我自己给这个材质取的名字,既然是积雪,那顾名思义,雪是从天而降的,因此积雪都是在物体朝上的表面:不管你的模型怎么摆放 ,雪都保证是积在物体朝上(在unity里就是y轴正方向)的表面,如下图所示: 实现原理 要保证向上的面有积雪,其实就是模型表面的法线方向与世界坐标空间的Y轴正方向保持一致积雪多,否则积雪就少雪,所以将模型的法线方向normal从模型空间转化到世界空间,然后与y轴正方向float3(0,1,0)做点积,根据结果来确认法相的朝向和Y轴的夹角大小,从而确认是否

Unity3d 屏幕特效实现类似死亡之后的全屏黑白效果

全屏特效 黑白(对于<着色器和屏幕特效开发秘籍>的学习) 可实现死亡效果或需要黑白特效的效果 原理是通过OnRenderImage()函数在摄像机渲染的时候,改变颜色(饱和度) 新建一个c#脚本,将要放在摄像机中 [ExecuteInEditMode] 就是让这个脚本不开始也直接运行,方便观察效果 using UnityEngine; using System.Collections; [ExecuteInEditMode] public class ShaderTest : MonoBeha

Oracle Study之--Oracle 单实例11.2.0.1.0升级到11.2.0.3.0

Oracle Study之--Oracle 单实例11.2.0.1.0升级到11.2.0.3.0 系统环境: 操作系统:RedHat EL6(64位) Oracle:    Oracle 11gR2 Oracle 11.2.0.1 单实例升级到11.2.0.3 Oracle 升级的步骤都差不多,先升级Oracle software,然后升级Oracle instance. Oracle 11.2.0.2的patchset No 是:10098816. 关于该Patchset 的下载地址,参考下面

Python 练习实例11

Python 练习实例11 题目:古典问题:有一对兔子,从出生https://www.xuanhe.net/第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 程序分析:兔子的规律为数列1,1,2,3,5,8,13,21.... 程序源代码: 以上实例输出结果为: 原文地址:https://www.cnblogs.com/danjiu/p/12143987.html

unity3d 老电影式的屏幕特效

做出这种古老效果,需要: 颜色发黄, 晕影效果, 灰尘与刮痕的效果. 建立一个c#脚本,将要放在摄像头中 先声明变量 oldFilmShader    所需shader(一会需要编写) OldFilmEffectAmount    古老的程度(最后做插值的程度值) sepiaColor    屏幕的颜色(调至黑黄) vigentteTexture        老电影式基本贴图 vigentteAmount         所占比重 scratchesTexture    刮痕贴图 scratc

11 攻击特效与击中反应-模板方法模式(Template Method)

1 /// <summary> 2 /// 定义完整算法的各个步骤及执行顺序 3 /// </summary> 4 public abstract class AbstractClass { 5 public void TemplateMethod() { 6 PrimitiveOperation1(); 7 PrimitiveOperation2(); 8 } 9 10 protected abstract void PrimitiveOperation1(); 11 prote

UnityShader实例08:广告牌(Billboard)材质

广告牌(Billboard)材质 Billboard概述 Billboard技术在游戏引擎中占有很重要的地位,一般用在粒子效果或者光晕效果上,让粒子面片z轴朝向摄影机.Unity里面也不例外 ,在Unity的粒子系统中的render面板的render mode中就四中billboard模式可选,这些模式都是面对摄影机的,只是轴向的约束不同:另外Unity的Terrain系统里也有Billboard技术的使用:但是如果我们使用自己做的模型或者预设,要使它朝向摄影机的话,则得自己写脚本或者写相应的s

图形化编程娱乐于教,Kittenblock实例,随机数特效

跟很多学生聊过,很多学生不是不努力,只是找不到感觉.有一点不可否认,同样在一个教室上课,同样是一个老师讲授,学习效果迥然不同.关键的问题在于,带入感,我能给出的建议,就是咬咬牙,坚持住,没有学不会的知识.会陆续分享图形化编程的经验,希望能够做成一个专题.如果您觉得有用,就给点个赞吧.涉及的软件包括scratch3.0 (.sb3).Python.Kittenblock. 程序解读:随机数特效 知识点:图章.循环.颜色特效.画笔模块.创建文字角色 涉及的软件:scratch3.0 (.sb3).P