读一读

Shader "Custom/lookSuface" 
{
	Properties
	{
		_Color ("Main Color",Color) = (1,1,1,1)
		_MainTex("Base (RGB)",2D) = "white"{}
		_BumpMap("Normalmap",2D) = "bump" {}
	}

	SubShader
	{
		Tags{"RenderType" = "Opaque"}
		LOD 300

		CGPROGRAM
		#pragma surface surf Lambert  //定义表面着色函数,指定使用兰伯特光照模型  
		#pragma target 3.0

		sampler2D _MainTex;
		sampler2D _BumpMap;
		fixed4 _Color;

		struct Input
		{
			float2 uv_MainTex;
			float2 uv_BumpMap;
		};

		void surf(Input IN, inout SurfaceOutput o)
		{
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb * _Color.rgb;
			o.Alpha = tex.a * _Color.a;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		}
		ENDCG
	}

	FallBack "Diffuse"
}


blob.png


Shader "Unlit/Dissolve"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}//主贴图
		_BurnAmount ("BurnAmount",Range(0.0,1.0)) = 0.0 //消融程度
		_LineWidth("Burn Line Width",Range(0.0,0.2)) = 0.1 //越大消融边界的颜色越多
		_BumpMap("Normal Map",2D) = "bump"{} //法线贴图
		_BurnFirstColor("Burn First Color",Color)=(1,0,0,1) //消融边界颜色1
		_BurnSecondColor("Burn Second Color",Color) = (1,0,0,1) //消融边界颜色2
		_BurnMap("Burn Map",2D) = "white"{} //噪音贴图
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			Tags{"LightMode" = "ForwardBase"}
			Cull Off//双面渲染
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdbase
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex:TEXCOORD0;
				float2 uvBumpMap:TEXCOORD1;
				float2 uvBurnMap:TEXCOORD2;
				float3 lightDir:TEXCOORD3;
				float3 worldPos:TEXCOORD4;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _BurnAmount;
			float _LineWidth;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			float4 _BurnFirstColor;
			float4 _BurnSecondColor;
			sampler2D _BurnMap;
			float4 _BurnMap_ST;
			
			v2f vert (appdata_full v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
				o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);

				TANGENT_SPACE_ROTATION;//使用切线空间的法线,把光照方向转到切线空间来计算漫反射
				o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed3 burn = tex2D(_BurnMap,i.uvBurnMap).rgb;
				clip(burn.r - _BurnAmount);//根据噪音贴图的取样结果 剪裁掉片元 消融 <0就会舍弃掉
				float3 tangentLightDir = normalize(i.lightDir);
				fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
				fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));

				//t为0表示为正常颜色 _LineWidth越大时smoothstep计算出来的结果就越小几率为1
				//t为0的几率就越小,所以边缘的颜色范围会越大
				fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
				fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);//两种颜色渐变,模拟燃烧腐蚀什么的
				burnColor = pow(burnColor, 5);//让边缘效果越好

				UNITY_LIGHT_ATTENUATION(atten, i,i.worldPos);
				//为0为正常颜色,step保证_BurnAmount为0时全是正常颜色
				fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t*step(0.0001, _BurnAmount));

				return fixed4(finalColor, 1);
			}
			ENDCG
		}
	}
}


11.gif


Shader "Unlit/KaTong"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}//主帖图
		_Color("Color Tint",Color) = (1,1,1,1)//颜色
		_Ramp("Ramp Texture",2D) = "white" {}//渐变贴图
		_Outline("Outline",Range(0,1)) = 0.1//描边大小
		_OutlineColor ("Outline Color",Color) = (1,1,1,1) //描边的颜色
		_Specular ("Specular",Color) = (1,1,1,1) //高光颜色
		_SpecularScale("Specular Scale",Range(0,0.1)) = 0.01 //高光缩放,范围
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			NAME "OUTLINE"
			Cull Front // 渲染背面,大一点,把边缘露出来
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal :NORMAL;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};

			float4 _OutlineColor;
			float _Outline;
			
			v2f vert (appdata v)
			{
				v2f o;
				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);//变换顶点到观察空间
				//变换法线到观察空间,UNITY_MATRIX_IT_MV是专门针对法线的变换矩阵,法线非等比变换会使角度改变,所以使用的矩阵和顶点变换的是不一样的
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
				//对法线的z轴处理在normalize 是为了防止背面顶点扩张挡住正面
				normal.z = -0.5;
				pos = pos + float4(normalize(normal), 0) * _Outline;
				o.vertex = mul(UNITY_MATRIX_P, pos);//在变换到检查矩阵
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//渲染成描边颜色就可以了
				return float4(_OutlineColor.rgb,1);
			}
			ENDCG
		}

		Pass{
			Tags{"LightMode"="ForwardBase"}

			Cull Back

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdbase

			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv:TEXCOORD0;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos : TEXCOORD2;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _Ramp;
			float4 _Color;
			float4 _Specular;
			fixed _SpecularScale;

			v2f vert(appdata_base v)
			{
				//单单的变换坐标,计算世界顶点位置和世界顶点法线给片元
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.worldNormal = mul(unity_ObjectToWorld, v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}

			float4 frag(v2f i):SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);

				fixed4 c = tex2D(_MainTex, i.uv);
				fixed3 albedo = c.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);//光照衰减

				fixed diff = dot(worldNormal, worldLightDir);
				diff = (diff * 0.5 + 0.5) * atten; //半兰伯特漫反射
				fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb; //融合渐变纹理和环境颜色

				//卡通的高光都是一块一块的,也就是让它不是0就是1,然后在边缘的地方进行一些过度,减少突变
				fixed spec = dot(worldNormal, worldHalfDir);
				fixed w = fwidth(spec) * 2.0; //邻域像素之间的近似倒数值,用来作为平滑的区域
				//smoothstep 在[-w,w]外区域外的话 >w就为1,<-w的话就为0 在这区间的话就平滑为0-1
				fixed3 specular = _Specular.rgb * lerp(0, 1, smoothstep(-w, w, spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);
				//step(0.0001, _SpecularScale) 是为了_SpecularScale为0时,消除高光

				return fixed4(ambient + diffuse + specular, 1.0);
			}
			ENDCG
		}
	}
	Fallback Off
}


一个卡通效果思路就是,在漫反射的时候使用一个渐变纹理控制色调,高光是区块性的,再加一个描边效果。这里使用的是过程式几何轮廓线渲染,就是使用一个Pass用来渲染较大的背面当作边缘线,再用另外一个Pass渲染正面。

blob.png


使用Roberts算子来进行卷积计算,本质是计算左上角和右下角的差值*右上角和左下角的差值来作为边缘评估的依据。在计算中,我们评估对比(左上角和右下角)、(左下角和右上角)的法线值和深度值的差异都超过某个阔值,就认为这里就是一条边缘。

using UnityEngine;

public class MiaoBianTwo : MonoBehaviour {

    public Material mat;

    [Range(0.0f, 1.0f)]
    public float edgesOnly = 0.0f;//是否只显示边缘
    public Color backgroundColor = Color.white;//只显示边缘时的背景颜色
    public Color edgeColor = Color.black;//描边的颜色
    public float sampleDistance = 1.0f; //卷积取样的距离,越大描边会越大
    public float sensitivityDepth = 1.0f;//深度值的阔值控制
    public float sensitivityNormals = 1.0f;//法线的阔值控制

    private void OnEnable()
    {
        this.GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
    }

    [ImageEffectOpaque]
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            mat.SetFloat("_EdgeOnly", edgesOnly);
            mat.SetColor("_EdgeColor", edgeColor);
            mat.SetColor("_BackgroundColor", backgroundColor);
            mat.SetFloat("_SampleDistance", sampleDistance);
            mat.SetVector("_Sensitivity", new Vector4(sensitivityNormals, sensitivityDepth, 0f, 0f));
            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


Shader "Unlit/MiaoBianTwo"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_EdgeOnly("Edge Only",Float) = 1.0
		_EdgeColor("Edge Color",Color) = (0,0,0,1)
		_BackgroundColor("Background Color",Color) = (1,1,1,1)
		_SampleDistance("Sample Distance",Float) = 1.0
		_Sensitivity("Sensitivity",Vector) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		CGINCLUDE
		sampler2D _MainTex;
		half4 _MainTex_TexelSize;
		fixed _EdgeOnly;
		fixed4 _EdgeColor;
		fixed4 _BackgroundColor;
		float _SampleDistance;
		half4 _Sensitivity;
		sampler2D _CameraDepthNormalsTexture;

		#include "UnityCG.cginc"

		struct v2f {
			float4 pos : SV_POSITION;
			half2 uv[5]:TEXCOORD0;
		};

		v2f vert(appdata_img v)
		{
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			half2 uv = v.texcoord;
			o.uv[0] = uv;
			#if UNITY_UV_STARTS_AT_TOP
						if (_MainTex_TexelSize.y < 0)
							uv.y = 1 - uv.y;
			#endif

			o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1, 1) * _SampleDistance;//右上角
			o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1, -1) * _SampleDistance;//左下角
			o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 1) * _SampleDistance;//左上角
			o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1, -1) * _SampleDistance;//右下角

			return o;
		}

		//对比两点深度值和法线值的差异
		half CheckSame(half4 center, half4 sample)
		{
			half2 centerNormal = center.xy;//法线不需要解包,这里只需要对比差异
			float centerDepth = DecodeFloatRG(center.zw);
			half2 sampleNormal = sample.xy;
			float sampleDepth = DecodeFloatRG(sample.zw);

			//控制阔值的值越小,差异化就会就越小,边缘就越少
			half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x;
			int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1; //差异小于0.1,

			float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y;
			int isSameDepth = diffDepth < 0.1 * centerDepth;

			return isSameDepth * isSameNormal ? 1.0 : 0.0;
		}

		fixed4 fragRobertsCrossDepthAndNormal(v2f i):SV_Target
		{
			half4 sample1 = tex2D(_CameraDepthNormalsTexture,i.uv[1]);
			half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2]);
			half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3]);
			half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4]);

			half edge = 1.0;
			edge *= CheckSame(sample1, sample2);
			edge *= CheckSame(sample3, sample4);

			//edge为1表示差异化小,显示原来颜色,为0表示为描边颜色
			fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), edge);
			fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);

			return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
		}
		ENDCG

		Pass
		{
			ZTest Always Cull Off ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment fragRobertsCrossDepthAndNormal
			ENDCG
		}
	}

	Fallback Off
}


blob.png

blob.png


已知,一个坐标系中的顶点坐标可以通过它相对于另一个顶点坐标的偏移来求得。我们可以先知道摄像机在世界坐标下的位置,以及世界空间下该像素相对于摄像机的偏移量,把它们相加就可以得到改像素的世界坐标。

float4 worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;

linearDepth是由深度纹理得到的线性深度值,interpolatedRay是由顶点着色器输出并插值得到的射线,它不仅包含了该像素到摄像机的方向,也包含了距离信息。


怎么求这个interpolatedRay就是重点了。interpolatedRay来源于近裁剪平面的4个角的某个特定向量的插值,这4个向量包含了它们到摄像机的方向和距离信息,最后根据顶点比较偏向哪个角,就用那边的特定向量的线性插值来计算世界坐标。

//近剪裁面中心到top边的垂直距离
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
//近剪裁面中心垂直指向左边
Vector3 toRight = cam.transform.right * halfHeight * aspect;
//近剪裁面中心垂直指向上边
Vector3 toTop = cam.transform.up * halfHeight;

Vector3 topLeft = cam.transform.forward * near - toRight + toTop;
float scale = topLeft.magnitude / near;//计算一个depth值转换成真实长度的scale

//相机指向近剪裁面的左上角
topLeft.Normalize();
topLeft *= scale;

//相机指向近剪裁面的右上角
Vector3 topRight = cam.transform.forward * near + toRight + toTop;
topRight.Normalize();
topRight *= scale;

//相机指向近剪裁面的右下角
Vector3 bottomRight = cam.transform.forward * near + toRight - toTop;
bottomRight.Normalize();
bottomRight *= scale;


各个方向乘以scale是因为通过深度贴图获取的linearDepth只是在z轴上的,而不是两点之间的距离,所以通过这个scale来转换过来,scale的由来(近似三角形)


using UnityEngine;

public class BackWu : MonoBehaviour {

    public Material mat;

    private Camera cam;

    [Range(0.0f, 3.0f)]
    public float fogDensity = 1.0f;//雾效强度

    public Color fogColor = Color.white;//雾的颜色

    public float fogStart = 0.0f; //开始高度
    public float fogEnd = 2.0f; //结束高度

	void Start () {
        cam = this.GetComponent<Camera>();
        cam.depthTextureMode |= DepthTextureMode.Depth;
	}

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            Matrix4x4 frustumCorners = Matrix4x4.identity;

            float fov = cam.fieldOfView;
            float near = cam.nearClipPlane;
            float far = cam.farClipPlane;
            float aspect = cam.aspect;

            //近剪裁面中心到top边的垂直距离
            float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
            //近剪裁面中心垂直指向左边
            Vector3 toRight = cam.transform.right * halfHeight * aspect;
            //近剪裁面中心垂直指向上边
            Vector3 toTop = cam.transform.up * halfHeight;

            Vector3 topLeft = cam.transform.forward * near - toRight + toTop;
            float scale = topLeft.magnitude / near;//计算一个depth值转换成真实长度的scale

            //相机指向近剪裁面的左上角
            topLeft.Normalize();
            topLeft *= scale;

            //相机指向近剪裁面的右上角
            Vector3 topRight = cam.transform.forward * near + toRight + toTop;
            topRight.Normalize();
            topRight *= scale;

            //相机指向近剪裁面的右下角
            Vector3 bottomRight = cam.transform.forward * near + toRight - toTop;
            bottomRight.Normalize();
            bottomRight *= scale;

            //相机指向近剪裁面的左下角
            Vector3 bottomLeft = cam.transform.forward * near - toRight - toTop;
            bottomLeft.Normalize();
            bottomLeft *= scale;

            //存储在一个矩阵中
            frustumCorners.SetRow(0, bottomLeft);
            frustumCorners.SetRow(1, bottomRight);
            frustumCorners.SetRow(2, topRight);
            frustumCorners.SetRow(3, topLeft);

            mat.SetMatrix("_FrustumCornersRay", frustumCorners);
            mat.SetFloat("_FogDensity", fogDensity);
            mat.SetColor("_FogColor", fogColor);
            mat.SetFloat("_FogStart", fogStart);
            mat.SetFloat("_FogEnd", fogEnd);

            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


Shader "Unlit/BackMohu"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_FogDensity("Fog Density",Float) = 1.0
		_FogColor("Fog Color",Color) = (1,1,1,1)
		_FogStart("Fog Start",Float) = 0.0
		_FogEnd("Fog End",Float) = 1.0
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		CGINCLUDE
		float4x4 _FrustumCornersRay;
		sampler2D _MainTex;
		half4 _MainTex_TexelSize;
		sampler2D _CameraDepthTexture;
		half _FogDensity;
		fixed4 _FogColor;
		float _FogStart;
		float _FogEnd;

		#include "UnityCG.cginc"

		struct v2f {
			float4 pos:SV_POSITION;
			half2 uv:TEXCOORD0;
			half2 uv_depth:TEXCOORD1;
			float4 interpolateRay:TEXCOORD2;
		};

		v2f vert(appdata_img v)
		{
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.uv_depth = v.texcoord;

			#if UNITY_UV_STARTS_AT_TOP
						if (_MainTex_TexelSize.y < 0)
							o.uv_depth = 1 - o.uv_depth.y;
			#endif

			int index = 0;
			if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5)
			{
				index = 0;
			}
			else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5)
			{
				index = 1;
			}
			else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5)
			{
				index = 2;
			}
			else
			{
				index = 3;
			}

			#if UNITY_UV_STARTS_AT_TOP
						if (_MainTex_TexelSize.y < 0)
							index = 3 - index;
			#endif
			
			//根据uv位置 来决定使用哪个Ray 并会线性插值到片元
			o.interpolateRay = _FrustumCornersRay[index];
			return o;
		}

		fixed4 frag(v2f i):SV_Target
		{
			float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth));
			float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolateRay.xyz;//根据深度值 重建世界坐标
			float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);//雾效的影响 基于高度线性计算
			fogDensity = saturate(fogDensity * _FogDensity);

			fixed4 finalColor = tex2D(_MainTex, i.uv);
			finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
			return finalColor;
		}
		ENDCG

		Pass
		{
			ZTest Always Cull Off ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
	}

	Fallback off
}


blob.png


using UnityEngine;

public class depthmohu : MonoBehaviour {

    public Material mat;

    [Range(0.0f,1.0f)]
    public float blurSize = 0.5f;

    private Matrix4x4 previousViewProjectionMatrix;  //上一次的视角投影矩阵
    private Camera c; //要实时获取它的视角矩阵和投影矩阵

	void Start () {
        c = this.GetComponent<Camera>();
        c.depthTextureMode = DepthTextureMode.Depth;
	}

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            mat.SetFloat("_BlurSize", blurSize); //模糊的程度,现在应该叫做幻影的程度
            mat.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);//上一帧的视角投影矩阵
            Matrix4x4 currentViewProjectionMatrix = c.projectionMatrix * c.worldToCameraMatrix;//当前帧的视角投影矩阵
            Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;//反转当前帧的视角投影矩阵

            mat.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
            previousViewProjectionMatrix = currentViewProjectionMatrix; //保存当前帧的视角投影矩阵

            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


Shader "Unlit/depthmohu"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_BlurSize("BlurSize",Float) = 0.5
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		CGINCLUDE
		#include "UnityCG.cginc"
		sampler2D _MainTex;
		half4 _MainTex_TexelSize;
		sampler2D _CameraDepthTexture;
		float4x4 _CurrentViewProjectionInverseMatrix;
		float4x4 _PreviousViewProjectionMatrix;
		half _BlurSize;
		struct v2f
		{
			float2 uv : TEXCOORD0;
			float2 uv_depth:TEXCOORD1;
			float4 vertex : SV_POSITION;
		};

		v2f vert(appdata_img v)
		{
			v2f o;
			o.vertex = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.uv_depth = v.texcoord;

			//平台差异化处理,贴图反转,防止开启了抗锯齿
			#if UNITY_UV_STARTS_AT_TOP
						if (_MainTex_TexelSize.y < 0)
							o.uv_depth.y = 1 - o.uv_depth.y;
			#endif

			return o;
		}

		fixed4 frag(v2f i):SV_Target
		{
			//深度贴图的取样
			float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
			//当前NDC的坐标,映射到[-1,1]范围
			float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
			//通过视角和投影的逆矩阵来得到世界坐标
			float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
			float4 worldPos = D / D.w;
			float4 currentPos = H;
			//通过上一帧的视角和投影矩阵,反构建上一帧的NDC坐标
			float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
			previousPos /= previousPos.w;

			//利用两个NDC坐标差异构建速度值
			float2 velocity = (currentPos.xy - previousPos.xy) / 2.0f; 
			float2 uv = i.uv;
			float4 c = tex2D(_MainTex, uv);
			for (int it = 1; it < 3; it++)
			{
				uv += velocity * _BlurSize;//速度值会决定模糊的程度,取样的偏差位置
				float4 currentColor = tex2D(_MainTex,uv);
				c += currentColor;
			}

			c /= 3;

			return fixed4(c.rgb, 1);
		}

		ENDCG

		Pass
		{
			ZTest Always Cull Off ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
	}

	Fallback Off
}


因为这是根据相机的视角和投影矩阵来反推世界坐标的,所以这个只适合这两个矩阵改变的运动迷糊,也就是说适合场景静止的情况下,相机动的运动模糊。


using UnityEngine;

public class ShowCameraDepthTexture : MonoBehaviour {

    public Material mat;

    private void Awake()
    {
        Camera camera = this.GetComponent<Camera>();
        //只要将相机的这个参数设置为DepthNormals就可以在Shader中使用_CameraDepthNormalsTexture访问深度贴图了
        camera.depthTextureMode = DepthTextureMode.DepthNormals;
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


Shader "Unlit/DepthTexShow"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _CameraDepthNormalsTexture;
			
			v2f vert (appdata_img v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float d;
				float3 normal;
				float4 enc = tex2D(_CameraDepthNormalsTexture,i.uv);
				DecodeDepthNormal(enc,d,normal);//获取深度值和法线值
				return fixed4(normal * 0.5 + 0.5, 1);
				//return fixed4(d,d,d, 1);
			}
			ENDCG
		}
	}
}


通过DecodeDepthNormal去解析取样的结果,可以获得out的深度值和法线方向,enc中xy存储的是视角空间下的法线信息,zw存储的是深度信息。

inline void DecodeDepthNormal( float4 enc, out float depth, out float3 normal )
{    
    depth = DecodeFloatRG (enc.zw);    
    normal = DecodeViewNormalStereo (enc);
}


blob.png


using UnityEngine;

public class ShowCameraDepthTexture : MonoBehaviour {

    public Material mat;

    private void Awake()
    {
        Camera camera = this.GetComponent<Camera>();
        //只要将相机的这个参数设置为Depth就可以在Shader中使用_CameraDepthTexture访问深度贴图了
        camera.depthTextureMode = DepthTextureMode.Depth;
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}


Shader "Unlit/DepthTexShow"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _CameraDepthTexture;
			
			v2f vert (appdata_img v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//SAMPLE_DEPTH_TEXTURE 用来取样深度贴图
				float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
				d = Linear01Depth(d); //转换到线性空间,并且约束在0-1,原本是经过剪裁矩阵的,非线性
				return fixed4(d,d,d,1);//可视化
			}
			ENDCG
		}
	}
}


调节相机Camera的Clipping Planes的Far属性到最远物体可以看见的位置,因为如果差太远的话深度图就会约束到很小值的地方了(黑色)blob.png


using UnityEngine;

public class movemohu : MonoBehaviour {

    public Material mat;

    [Range(0.0f, 0.9f)]
    public float blurAmount = 0.5f;//与原图像混合的透明度

    private RenderTexture accumulationTexture;//上一次的渲染图

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (mat != null)
        {
            //当上一次的渲染图不存在或则说宽高都不一样了,说明重新开始场景了
            if (accumulationTexture == null || accumulationTexture.width != source.width || accumulationTexture.height != source.height)
            {
                accumulationTexture = new RenderTexture(source.width, source.height, 0);
                accumulationTexture.hideFlags = HideFlags.HideAndDontSave;

                Graphics.Blit(source, accumulationTexture);
            }

            //渲染纹理的恢复操作,发生在渲染的纹理时没有被清空的情况,这里需要和当前渲染的图像混合
            accumulationTexture.MarkRestoreExpected();
            mat.SetFloat("_BlurAmount", 1.0f - blurAmount);
            Graphics.Blit(source, accumulationTexture, mat);
            Graphics.Blit(accumulationTexture, destination);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }

    private void OnDisable()
    {
        DestroyImmediate(accumulationTexture);
    }
}


Shader "Unlit/movemohu"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_BlurAmount("Blur Amount",Float) = 1.0
	}

	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		CGINCLUDE
		#include "UnityCG.cginc"
		sampler2D _MainTex;
		fixed _BlurAmount;

		struct v2f
		{
			float2 uv : TEXCOORD0;
			float4 vertex : SV_POSITION;
		};

		v2f vert(appdata_img v)
		{
			v2f o;
			o.vertex = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			return o;
		}

		fixed4 fragRGB(v2f i):SV_Target
		{
			return fixed4(tex2D(_MainTex,i.uv).rgb,_BlurAmount);
		}

		half4 fragA(v2f i) : SV_Target
		{
			return tex2D(_MainTex,i.uv);
		}
		ENDCG

		ZTest Always Cull Off ZWrite Off
		Pass
		{
			Blend SrcAlpha OneMinusSrcAlpha
			ColorMask RGB //不写入透明通道,但使用自定义的透明通道混合
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment fragRGB
			ENDCG
		}

		Pass 
		{
			Blend One Zero
			ColorMask A //写入原来的透明通道

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment fragA
			ENDCG
		}
	}
}



GIF.gif