Shader收藏大杂烩

翻书效果

  1. 绕Z轴旋转
    修改 _Angle 大小,来旋转平面
Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        //旋转角度
        _Angle("Angle",Range(0,180))=0
    }
 	....
    sampler2D _MainTex;
    //角度
    float _Angle;
	//顶点着色器
    v2f vert (appdata v)
    {
        v2f o;
        float s;
        float c;
        //通过该方法可以计算出该角度的正余弦值
		sincos(radians(_Angle),s,c);
        //旋转矩阵
        float4x4 rotateMatrix={			
            c ,s,0,0,
            -s,c,0,0,
            0 ,0,1,0,
            0 ,0,0,1
        };
        //顶点左乘以旋转矩阵
		v.vertex = mul(rotateMatrix,v.vertex);
		//模型空间转换到裁剪空间
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        return o;
    }
    ....

通过测试发现,这样的旋转并不是我们想要的效果,此时旋转的轴在中心,我们想让它的旋转轴在最左边,此时就需要把所有顶点在旋转之前都往左偏移5个单位,旋转完成之后再向右偏移5个单位就可以达到我们想要的效果了

  v2f vert (appdata v)
            {
                v2f o;
                //旋转之前向左偏移5个单位
				v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通过该方法可以计算出该角度的正余弦值
                sincos(radians(_Angle),s,c);
                //旋转矩阵
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //顶点左乘以旋转矩阵
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋转之后偏移回来
				v.vertex += float4(5,0,0,0);

                //模型空间转换到裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

现在的翻书效果太生硬了,为了接近真实的翻书效果,我们就需要通过正余弦函数修改顶点的y坐标,来达到一个弧度的效果。

v2f vert (appdata v)
 {
     v2f o;
     //旋转之前向右偏移5个单位
     v.vertex -= float4(5,0,0,0);
     float s;
     float c;
     //通过该方法可以计算出该角度的正余弦值
     sincos(radians(_Angle),s,c);
     //旋转矩阵
     float4x4 rotateMatrix={
         c ,s,0,0,
         -s,c,0,0,
         0 ,0,1,0,
         0 ,0,0,1
     };
     //根据x坐标,通过正弦函数计算出 y坐标的正弦值,  _WaveLength 控制波长, 振幅就跟随角度正弦值动态变化
     v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

     //顶点左乘以旋转矩阵
     v.vertex = mul(rotateMatrix,v.vertex);
     //旋转之后偏移回来
     v.vertex += float4(5,0,0,0);

     //模型空间转换到裁剪空间
     o.vertex = UnityObjectToClipPos(v.vertex);
     o.uv = v.uv;
     return o;
 }

仔细观察会发现“翻书”的过程,背面有点不真实,不应该是该纹理的反面,而是另一张新的纹理,此时我们该怎么办呢?其实很简单,只需要把正面和反面分开渲染就可以了,一个Pass渲染正面,一个Pass渲染背面。

完整代码

Shader "Learn Unity Shader/openBook"
{
    Properties
    {
        //正面纹理
        _MainTex ("Texture", 2D) = "white" {}
        //背面纹理
		_SecTex("SecTex",2D)="White"{}

        //旋转角度
        _Angle("Angle",Range(0,180))=0
        //波长
        _WaveLength("WaveLength",Range(-1,1))=0

    }
    SubShader
    {

        Pass
        {
            //剔除背面
			Cull Back

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            sampler2D _MainTex;
			float4 _MainTex_ST;
            //角度
            float _Angle;
            //波长
            float _WaveLength;

            v2f vert (appdata v)
            {
                v2f o;
                //旋转之前向右偏移5个单位
                v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通过该方法可以计算出该角度的正余弦值
                sincos(radians(_Angle),s,c);
                //旋转矩阵
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //根据x坐标,通过正弦函数计算出 y坐标的正弦值,  _WaveLength 控制波长, 振幅就跟随角度正弦值动态变化
                v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

                //顶点左乘以旋转矩阵
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋转之后偏移回来
                v.vertex += float4(5,0,0,0);

                //模型空间转换到裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }


            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }

         Pass
        {
            //剔除正面
			Cull Front

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

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

            //角度
            float _Angle;
            //波长
            float _WaveLength;

            sampler2D _SecTex;
			float4 _SecTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                //旋转之前向右偏移5个单位
                v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通过该方法可以计算出该角度的正余弦值
                sincos(radians(_Angle),s,c);
                //旋转矩阵
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //根据x坐标,通过正弦函数计算出 y坐标的正弦值,  _WaveLength 控制波长, 振幅就跟随角度正弦值动态变化
                v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

                //顶点左乘以旋转矩阵
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋转之后偏移回来
                v.vertex += float4(5,0,0,0);

                //模型空间转换到裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }


            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_SecTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}


压扁效果

实现原理:
1、通过脚本求得物体的的最高顶点和最低顶点;
2、传给shader,得到最高顶点和最低顶点的范围,并计算各顶点到最高顶点的距离归一化比;
3、在通过一个参数,来控制压缩程度,即是 模型 顶点 y的偏移程度,实现压缩效果;
注意事项:
1、 使用 max(0,_Control-normalizedDist) 来调整压缩
2、因为使用了归一化顶点比例,所以最终偏移顶点y 的时候需要乘以一个 最高顶点与最低顶点的距离;
3、因为这是世界位置上的压缩,所以记得把计算的顶点位置添加上 _ObjectWorldPosY;
4、必要可以取消归一化那一步骤,就把不需要最后再乘以 Range_Top_Bottom;

// https://www.freesion.com/article/7411549136/
Shader "Kaima/Other/Squash"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_TopY("Top Y", Float) = 0    //The top Y of the GameObject in world coord
		_BottomY("Bottom Y", Float) = 0 
		_Control("Control Squash", Range(0, 1)) = 0 //control the level of squash
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _TopY;
			float _BottomY;
			float _Control;

			float GetNormalizedDist(float worldPosY)
			{
				float range = _TopY - _BottomY;
				float border = _TopY;

				float dist = abs(worldPosY - border);
				float normalizedDist = saturate(dist / range);
				return normalizedDist;
			}
			
			v2f vert (appdata v)
			{
				v2f o;
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				float worldPosY = mul(unity_ObjectToWorld, v.vertex).y;
				//float3 localNegativeY = mul(unity_WorldToObject, float4(0, -1, 0, 1)).xyz;
				float normalizedDist = GetNormalizedDist(worldPosY);

				 压缩比例(至于为什么max 是因为压缩normalizedDist 为0-1, _Control - 为负,
				//说明未压缩到,故取0,不然就反置压扁,你可以试试不要 max 看看结果)
				float val = max(0, _Control - normalizedDist);
				//v.vertex.xyz += localNegativeY * val;
				v.vertex.y -= val;    // v.vertex.y += val;
				 因为Y 值上压扁变化,故减掉 y 上的压扁的距离(注意乘上 顶点的距差(不然,不乘以距离差,你可以试试capsule会压不扁))
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				return col;
			}
			ENDCG
		}
	}
}

使用

public class ToySquashY : MonoBehaviour
{
    void Start()
    {
        Material mat = GetComponent<Renderer>().material;
 
 
        float minY, maxY;
        CalculateMinMaxY(out minY, out maxY);
        mat.SetFloat("_BottomY", minY);
        mat.SetFloat("_TopY", maxY);
        mat.SetFloat("_ObjectWorldPosY", transform.position.y);
    }
 
    /// <summary>
    /// 计算模型 Y 最高值顶点和最小值顶点
    /// </summary>
    /// <param name="minY"></param>
    /// <param name="maxY"></param>
    void CalculateMinMaxY(out float minY, out float maxY)
    {
        Vector3[] vertices = GetComponent<MeshFilter>().mesh.vertices;
        minY = maxY = vertices[0].y;
        for (int i = 1; i < vertices.Length; i++)
        {
            Vector3 tmp =  vertices[i];
            float y = tmp.y;
            if (y < minY)
                minY = y;
            if (y > maxY)
                maxY = y;
        }
 
        // 转到世界坐标上去
        minY += transform.position.y;
        maxY += transform.position.y;
    }
}

物体被地面吸收效果

实现原理:

  1. 通过脚本求得物体的最高点和最低点
  2. 计算各顶点到最高点的距离归一化
  3. 使用参数控制物体吸收的程度, 范围 0-2
  4. 对地里面的部分 clip
    注意:
    _Control = sin(_Time.y * _Speed ) + 1; 用来代码模拟的,正式用的时候可以用其他方法代替;
Shader "Kaima/Other/BornFromY"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_TopY("Top Y", Float) = 0
		_BottomY("Bottom Y", Float) = 0
		_Control("Born Control", Range(0, 2)) = 0
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }

		Pass
		{
			Cull Off

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _TopY;
			float _BottomY;
			float _Control;


			//计算到上边界的值
			float GetNormalizedDist(float worldPosY)
			{
				float range = _TopY - _BottomY;
				float border = _TopY;

				float dist = abs(worldPosY - border);
				float normalizedDist = saturate(dist / range);
				return normalizedDist;
			}
			
			v2f vert (appdata v)
			{
				v2f o;
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				float3 localPositiveY = mul(unity_WorldToObject, float4(0, 1, 0, 1)).xyz;
				float normalizedDist = GetNormalizedDist(worldPos.y);
				float val = max(0, _Control - normalizedDist);
				v.vertex.xyz += localPositiveY * val;

				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				clip(_TopY - i.worldPos.y);
				fixed4 col = tex2D(_MainTex, i.uv);
				return col;
			}
			ENDCG
		}
	}
}

黑洞吸收效果

Shader "Unlit/ShaderBlackHole"
{
     Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_NearPos("Near Postion",Vector) =(0,0,0,0)
		_FarPos("Far Position",Vector)=(0,0,0,0)
		_Control("Control Squash",Range(0,2))=0
		_Speed("Speed",Range(0,10))=1
		_ObjectWorldPos("Object World Position",Vector)=(0,0,0,0)
		_BlackHolePos("Black Hole Position",Vector)=(0,0,0,0)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
 
        Pass
        {
           CGPROGRAM
		   #pragma vertex vert
		   #pragma fragment frag
 
		   #include "UnityCG.cginc"
 
		   struct appdata{
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
		   };
 
		   struct v2f{
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
				float3 worldPos:TEXCOORD1;
			
		   };
 
		   sampler2D _MainTex;
		   float4 _MainTex_ST;
 
		   float3 _NearPos;
		   float3 _FarPos;
		   float3 _BlackHolePos;
 
		   float _Control;
		   float _Speed;
		   float rangeFar_Near;
 
		   // 求顶点到最高顶点的距离比例
		   float GetNormalizedDist(float3 worldPos){
				// 加上在世界坐标的位置
				worldPos = worldPos ;
 
				// 最大最小范围
				rangeFar_Near = length(_FarPos-_NearPos);
 
				// 以最高顶点为黑洞吸收点开始点
				float3 border = _NearPos;
 
				// 求顶点据最高顶点的距离绝对值
				float dist = length( worldPos-border);
 
				//归一化距离值
				float normalizedDist = saturate(dist/rangeFar_Near);
 
				return normalizedDist;
		   }
 
		   v2f vert(appdata v){
				
				v2f o;
				o.uv = TRANSFORM_TEX(v.uv,_MainTex);
 
				// 把顶点撞到世界坐标上
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
				
 
				// 得到顶点距离顶部的比例
				float normalizedDist = GetNormalizedDist(worldPos);
				
				// sin 值范围为 (-1,1),+1 使得范围再(0,2)周期变化
				_Control = sin(_Time.y * _Speed ) + 1;
 
				// 变化比例(至于为什么max 是因为变化normalizedDist 为0-1, _Control - 为负,说明未变化到,故取0,不然就,你可以试试不要 max 看看结果)
				float val = max(0,_Control-normalizedDist);
				
				float3 toBlackHole = mul(unity_WorldToObject, (_BlackHolePos - worldPos)).xyz;
 
				float3 srcVertex = v.vertex.xyz;
				// 向黑洞方向偏移顶点数值,渐渐被黑洞吸收
				v.vertex.xyz += toBlackHole* val;
 
				// 因为 val 的值会大于1,所以当顶点偏移到黑洞就一直保持
				// length(v.vertex.xyz - srcVertex) 当前顶点偏移的值
				// _BlackHolePos - worldPos 顶点到黑洞的最大值
				// 故当 当前顶点偏移的值 大于 顶点到黑洞的最大值,让顶点值设置为黑洞处
				if(length(v.vertex.xyz - srcVertex) > length(_BlackHolePos - worldPos)){
					v.vertex.xyz = srcVertex+ toBlackHole;
				}
 
				// 转到裁剪空间
				o.vertex = UnityObjectToClipPos(v.vertex);
 
				
				return o;
 
		   }
 
		   fixed4 frag(v2f i):SV_Target{				
 
				fixed4 col = tex2D(_MainTex,i.uv);			
 
				return col;
		   }
 
		   ENDCG
 
        }
    }
}
 

残影

一般残影的实现原理: 在主题发生一定位移后,每间隔一段时间,克隆一段Mesh,用FadeShader进行渲染, 1秒后销毁
本文的残影实现不同于上面的原理!

Shader "Kaima/Other/GhostShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_GhostColor("Ghost Color", Color) = (1,1,1,1) //残影颜色
		_Offset("Offset", Range(0, 2)) = 0 //残影偏离本位的距离
		_GhostAlpha("Ghost Alpha", Range(0, 1)) = 1 //残影的透明度
		_ShakeLevel("Shake Level", Range(0, 2)) = 0 //残影抖动的程度
		_ShakeSpeed("Shake Speed", Range(0, 50)) = 1 //残影的移动速度
		_ShakeDir("Shake Direction", Vector) = (0, 0, 1, 0) //残影移动的方向
		_Control("Control", Range(0, 0.54)) = 0 //整体控制残影
	}
	SubShader
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }
//第一个通道渲染影子
		Pass //残影
		{
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 

			#include "UnityCG.cginc"

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

			sampler2D _MainTex;
			fixed4 _GhostColor;
			float _Offset;
			float _GhostAlpha;
			float _ShakeLevel;
			float _ShakeSpeed;
			float _Control;
			float4 _ShakeDir;

			v2f vert(appdata_base v)
			{
				float yOffset = 0.5 * (floor(v.vertex.x * 10) % 2);

				v2f o;
				v.vertex += _Offset * cos(_Time.y * _ShakeSpeed) * _ShakeDir * _Control; //偏移
				v.vertex += _ShakeLevel * yOffset * sin(_Time.y * _ShakeSpeed) * _ShakeDir * _Control; //抖动
				o.vertex = UnityObjectToClipPos(v.vertex);

				o.uv = v.texcoord;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(tex2D(_MainTex, i.uv).rgb * _GhostColor, _GhostAlpha);
			}

			ENDCG
		}

//第二个通道渲染角色
		Pass //普通着色
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				return tex2D(_MainTex, i.uv);
			}
			ENDCG
		}
	}
}

折纸

思路是对顶点以折叠处为中心进行旋转。如果只是折叠一边,则只旋转一边;如果折叠两边,则两边一起旋转

在这里插入图片描述

Shader "Kaima/Other/PaperFold"
{
	Properties
	{
		_FrontText ("Front Tex", 2D) = "white" {}
		_BackTex  ("Back Tex", 2D) = "white" {}
		_FoldPos ("Fold Pos", float) = 5
		_FoldAngle   ("Fold Angle", Range(1, 180)) = 90
		[Toggle(ENABLE_DOUBLE)] _DoubleFold ("Double Fold", Float) = 0
	}
	SubShader
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }

		Pass
		{
			ZWrite On
			Cull Back
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma shader_feature ENABLE_DOUBLE
			
			#include "UnityCG.cginc"

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

			sampler2D _FrontText;
			float4 _FrontText_ST;
			float _FoldPos, _FoldAngle;

			v2f vert (appdata_base v)
			{
				float angle = _FoldAngle;
				float r = _FoldPos - v.vertex.x;

				#if ENABLE_DOUBLE
					if(r <= 0)
						angle = 360 - _FoldAngle;
				#else
					if(r <= 0)
						angle = 180;
				#endif

				v.vertex.x = _FoldPos + r * cos(angle * UNITY_PI / 180);
				v.vertex.y = r * sin(angle * UNITY_PI / 180);
				
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _FrontText);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_FrontText, i.uv);
				return col;
			}
			ENDCG
		}

		Pass
		{
			ZWrite On
			Cull Front
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma shader_feature ENABLE_DOUBLE

			#include "UnityCG.cginc"

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

			sampler2D _BackTex;
			float4 _BackTex_ST;
			float _FoldPos, _FoldAngle;

			
			v2f vert (appdata_base v)
			{
				float angle = _FoldAngle;
				float r = _FoldPos - v.vertex.x;

				#if ENABLE_DOUBLE
					if(r <= 0)
						angle = 360 - _FoldAngle;
				#else
					if(r <= 0)
						angle = 180;
				#endif

				v.vertex.x = _FoldPos + r * cos(angle * UNITY_PI / 180);
				v.vertex.y = r * sin(angle * UNITY_PI / 180);
				 
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _BackTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_BackTex, i.uv);				
				return col;
			}
			ENDCG
		}
	}
}

消融效果

噪音纹理 + 透明度测试

Shader "Kaima/Dissolve/Basic"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off //要渲染背面保证效果正确

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r;
				clip(cutout - _Threshold);

				fixed4 col = tex2D(_MainTex, i.uvMainTex);
				return col;
			}
			ENDCG
		}
	}
}

改造 边缘颜色

Shader "Kaima/Dissolve/EdgeColor"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_EdgeColor("Edge Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off //要渲染背面保证效果正确

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			fixed4 _EdgeColor;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//镂空
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r;
               // 顶点裁剪
				clip(cutout - _Threshold);

				//边缘颜色
				if(cutout - _Threshold < _EdgeLength)
					return _EdgeColor;

				fixed4 col = tex2D(_MainTex, i.uvMainTex);
				return col;
			}
			ENDCG
		}
	}
}

混合2种颜色


Shader "Kaima/Dissolve/TwoEdgeColor"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_EdgeFirstColor("First Edge Color", Color) = (1,1,1,1)
		_EdgeSecondColor("Second Edge Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off //要渲染背面保证效果正确

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			fixed4 _EdgeFirstColor;
			fixed4 _EdgeSecondColor;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//镂空
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r;
				//裁剪
				clip(cutout - _Threshold);

				//边缘颜色
				if(cutout - _Threshold < _EdgeLength)
				{
				    //插值系数
					float degree = (cutout - _Threshold) / _EdgeLength;
					return lerp(_EdgeFirstColor, _EdgeSecondColor, degree);
				}

				fixed4 col = tex2D(_MainTex, i.uvMainTex);
				return col;
			}
			ENDCG
		}
	}
}

混合本来颜色颜色


Shader "Kaima/Dissolve/BlendOriginColor"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_EdgeFirstColor("First Edge Color", Color) = (1,1,1,1)
		_EdgeSecondColor("Second Edge Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off //要渲染背面保证效果正确

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			fixed4 _EdgeFirstColor;
			fixed4 _EdgeSecondColor;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//镂空
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r;
				//裁剪顶点
				clip(cutout - _Threshold);
				// 插值系数
				float degree = saturate((cutout - _Threshold) / _EdgeLength);
				//对颜色进行插值
				fixed4 edgeColor = lerp(_EdgeFirstColor, _EdgeSecondColor, degree);
				//纹理采样
				fixed4 col = tex2D(_MainTex, i.uvMainTex);
                //对最后的颜色进行插值
				fixed4 finalColor = lerp(edgeColor, col, degree);
				return fixed4(finalColor.rgb, 1);
			}
			ENDCG
		}
	}
}

使用渐变纹理

Shader "Kaima/Dissolve/Ramp"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_RampTex("Ramp", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off //要渲染背面保证效果正确

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
				float2 uvRampTex : TEXCOORD2;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				o.uvRampTex = TRANSFORM_TEX(v.uv, _RampTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//镂空
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r;
				clip(cutout - _Threshold);

				float degree = saturate((cutout - _Threshold) / _EdgeLength);
				//使用系数对渐变纹理采样
				fixed4 edgeColor = tex2D(_RampTex, float2(degree, degree));

				fixed4 col = tex2D(_MainTex, i.uvMainTex);

				fixed4 finalColor = lerp(edgeColor, col, degree);
				return fixed4(finalColor.rgb, 1);
			}
			ENDCG
		}
	}
}

从某一个点开始消融

为了从特定点开始消融,我们需要把片元到特定点的距离clip掉
1. 定义消融开始点,然后求出各片元到该点的距离
2. 求出网格内2个点的最大距离,用来对第一步求出的距离进行归一化。这一步需要在C#脚本中进行,思路就是遍历任意2点,然后找出最大距离
3. 第三步就是归一化距离值
4. 加入一个 _DistanceEffect从之消融的影响程度。
Shader "Kaima/Dissolve/FromPoint"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}   //主纹理
		_NoiseTex("Noise", 2D) = "white" {}    //噪音纹理
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5    //权值
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1   //边界宽度
		_RampTex("Ramp", 2D) = "white" {}    //渐变纹理
		_StartPoint("Start Point", Vector) = (0, 0, 0, 0)   //开始顶点
		_MaxDistance("Max Distance", Float) = 0    //通过脚本计算最大距离
		_DistanceEffect("Distance Effect", Range(0.0, 1.0)) = 0.5   //控制消融程度
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off 

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
				float2 uvRampTex : TEXCOORD2;
				float3 objPos : TEXCOORD3;
				float3 objStartPos : TEXCOORD4;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			float _MaxDistance;
			float4 _StartPoint;
			float _DistanceEffect;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);

				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				o.uvRampTex = TRANSFORM_TEX(v.uv, _RampTex);

				o.objPos = v.vertex;  //对象空间
				o.objStartPos = mul(unity_WorldToObject, _StartPoint);  //开始点从世界空间转到对象空间

				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
			    //计算片元到开始点的距离
				float dist = length(i.objPos.xyz - i.objStartPos.xyz); 
				//对距离进行归一化
				float normalizedDist = saturate(dist / _MaxDistance);
				//下面这句有点难理解
				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r * (1 - _DistanceEffect) + normalizedDist * _DistanceEffect;
				clip(cutout - _Threshold);

				float degree = saturate((cutout - _Threshold) / _EdgeLength);
				//渐变采样
				fixed4 edgeColor = tex2D(_RampTex, float2(degree, degree));

				fixed4 col = tex2D(_MainTex, i.uvMainTex);

				fixed4 finalColor = lerp(edgeColor, col, degree);
				return fixed4(finalColor.rgb, 1);
			}
			ENDCG
		}
	}
}

场景切换


Shader "Kaima/Dissolve/ToPoint"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_RampTex("Ramp", 2D) = "white" {}
		_StartPoint("Start Point", Vector) = (0, 0, 0, 0)
		_MaxDistance("Max Distance", Float) = 0
		_DistanceEffect("Distance Effect", Range(0.0, 1.0)) = 0.5
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

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

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

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

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
				float2 uvRampTex : TEXCOORD2;
				float3 worldPos : TEXCOORD3;
				float3 worldNormal : TEXCOORD4;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			float _MaxDistance;
			float4 _StartPoint;
			float _DistanceEffect;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);

				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				o.uvRampTex = TRANSFORM_TEX(v.uv, _RampTex);
				//转到世界空间
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);

				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
			    //计算各个片元到世界空间的距离
				float dist = length(i.worldPos.xyz - _StartPoint.xyz);
				//归一化距离
				float normalizedDist = 1 - saturate(dist / _MaxDistance);

				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r * (1 - _DistanceEffect) + normalizedDist * _DistanceEffect;
				clip(cutout - _Threshold);

				float degree = saturate((cutout - _Threshold) / _EdgeLength);
				fixed4 edgeColor = tex2D(_RampTex, float2(degree, degree));

				//加点漫反射
				fixed4 albedo = tex2D(_MainTex, i.uvMainTex);
				float3 worldNormal = normalize(i.worldNormal);
				float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(worldNormal, worldLightDir));
				//对边缘色和漫反射进行插值
				fixed4 finalColor = lerp(edgeColor, fixed4(diffuse, 1), degree);
				return fixed4(finalColor.rgb, 1);
			}
			ENDCG
		}
	}
}

public class DissolveEnvironment : MonoBehaviour {
    public Vector3 dissolveStartPoint;
    [Range(0, 1)]
    public float dissolveThreshold = 0;
    [Range(0, 1)]
    public float distanceEffect = 0.6f;

    void Start () {
        //计算所有子物体到消融开始点的最大距离
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        float maxDistance = 0;
        for(int i = 0; i < meshFilters.Length; i++)
        {
            float distance = CalculateMaxDistance(meshFilters[i].mesh.vertices);
            if (distance > maxDistance)
                maxDistance = distance;
        }
        //传值到Shader
        MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();
        for(int i = 0; i < meshRenderers.Length; i++)
        {
            meshRenderers[i].material.SetVector("_StartPoint", dissolveStartPoint);
            meshRenderers[i].material.SetFloat("_MaxDistance", maxDistance);
        }
    }
    
    void Update () {
        //传值到Shader,为了方便控制所有子物体Material的值
        MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < meshRenderers.Length; i++)
        {
            meshRenderers[i].material.SetFloat("_Threshold", dissolveThreshold);
            meshRenderers[i].material.SetFloat("_DistanceEffect", distanceEffect);
        }
    }

    //计算给定顶点集到消融开始点的最大距离
    float CalculateMaxDistance(Vector3[] vertices)
    {
        float maxDistance = 0;
        for(int i = 0; i < vertices.Length; i++)
        {
            Vector3 vert = vertices[i];
            float distance = (vert - dissolveStartPoint).magnitude;
            if (distance > maxDistance)
                maxDistance = distance;
        }
        return maxDistance;
    }
}

从某个方向消融


Shader "Kaima/Dissolve/FromDirectionX"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Threshold("Threshold", Range(0.0, 1.0)) = 0.5
		_EdgeLength("Edge Length", Range(0.0, 0.2)) = 0.1
		_RampTex("Ramp", 2D) = "white" {}
		_Direction("Direction", Int) = 1 //1表示从X正方向开始,其他值则从负方向
		_MinBorderX("Min Border X", Float) = -0.5 //从程序传入
		_MaxBorderX("Max Border X", Float) = 0.5  //从程序传入
		_DistanceEffect("Distance Effect", Range(0.0, 1.0)) = 0.5
	}
	SubShader
	{
		Tags { "Queue"="Geometry" "RenderType"="Opaque" }

		Pass
		{
			Cull Off 

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

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvNoiseTex : TEXCOORD1;
				float2 uvRampTex : TEXCOORD2;
				float objPosX : TEXCOORD3;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseTex;
			float4 _NoiseTex_ST;
			float _Threshold;
			float _EdgeLength;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			int _Direction;
			float _MinBorderX;
			float _MaxBorderX;
			float _DistanceEffect;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);

				o.uvMainTex = TRANSFORM_TEX(v.uv, _MainTex);
				o.uvNoiseTex = TRANSFORM_TEX(v.uv, _NoiseTex);
				o.uvRampTex = TRANSFORM_TEX(v.uv, _RampTex);

				o.objPosX = v.vertex.x;

				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float range = _MaxBorderX - _MinBorderX;
				float border = _MinBorderX;
				if(_Direction == 1) //1表示从X正方向开始,其他值则从负方向
					border = _MaxBorderX;

				float dist = abs(i.objPosX - border);
				float normalizedDist = saturate(dist / range);

				fixed cutout = tex2D(_NoiseTex, i.uvNoiseTex).r * (1 - _DistanceEffect) + normalizedDist * _DistanceEffect;
				clip(cutout - _Threshold);

				float degree = saturate((cutout - _Threshold) / _EdgeLength);
				fixed4 edgeColor = tex2D(_RampTex, float2(degree, degree));

				fixed4 col = tex2D(_MainTex, i.uvMainTex);

				fixed4 finalColor = lerp(edgeColor, col, degree);
				return fixed4(finalColor.rgb, 1);
			}
			ENDCG
		}
	}
}

原文链接: ShaderDemo.
参考链接:灰信网
原文链接:

Contents

Other

Blog: Unity Shader - 一些玩具Shader

Squash:

Squash

BornFromY:

BornFromY场景

BlackHole:

BlackHole场景

GhostShader:

GhostShader场景

PaperFold:

PaperFold场景

Dissolve

Blog: Unity Shader - 消融效果原理与变体

DissolveFromPoint:

DissolveFromPoint场景

DissolveEnvironment:

DissolveEnvironment场景

DissolveFromDirection:

DissolveFromDirectionX场景

DirectionAsh:

DirectionAsh场景

Trifox:

Trifox场景

Bump

Blog: Unity Shader - 表面凹凸技术汇总

Parallax Occlusion Mapping, POM:

Parallax Mapping With Self Shadowing:

Displacement Mapping:

Displacement

Depth

Blog: Unity Shader - 深度图基础及应用

Scan Line:

ScanLine场景

Intersection Highlight:

IntersectionHighlight场景

Force Field:

ForceField场景

Fog:

Fog场景

EdgeDetection:

EdgeDetection场景

Vertical Fog:

VerticalFog场景

Motion Blur:

MotionBlur场景

Depth of Field:

DepthOfField场景

Shape

Blog: Unity Shader - 几何图形绘制

ManyShapes场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值