Shader之消融效果

简介

游戏中经常会看到特效给人眼前一亮的感觉,例如当怪物死亡时逐渐消逝的效果、人物由隐形状态逐渐显形的效果。本篇文章将从Shader的角度去思考如何通过代码去实现这些效果。

原理

  • 噪声图:利用噪声图的特性可以产生从随机点开始扩散的消融效果
  • 片元裁剪:利用Clip函数裁剪消融片元
    基本流程:定义一个阈值(DissolveThreshold)范围在[0,1],对噪声图进行纹理采样,将采样结果与阈值进行比较,如果大于阈值则正常显示,如果小于阈值则裁剪当前片元。当阈值从0递增到1时,消融范围逐步扩大实现逐渐消逝效果。当阈值从1递减到0时,消融范围逐步减少实现逐渐显形效果。

普通消融效果

Shader"MyShader/NormalDissolve"
{
	Properties
	{
		_Diffuse("Diffuse Color",Color) = (1,1,1,1)						//主颜色
		_MainTex("Main Tex",2D) = "white"{}								//主纹理
		_DissolveMap("Dissolve Map",2D) = "white"{}						//消融噪声
		_DissolveThreshold("Dissolve Threshold",Range(0.0,1.0)) = 0.0	//阈值
	}

	SubShader
	{
		Tags
		{
			"RenderType" = "Opaque"
			"Queue" = "Geometry"
		}

		//主Pass负责消融效果
		Pass
		{
			Tags{"LightMode"="ForwardBase"}

			//关闭背面剔除同时渲染模型正面与背面(避免消融裸露模型内部)
			Cull Off

			CGPROGRAM
			#include"Lighting.cginc"
			#include"AutoLight.cginc"

			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _DissolveMap;
			float4 _DissolveMap_ST;
			float _DissolveThreshold;


			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvMapTex : TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
			};

			v2f vert(a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uvMapTex = TRANSFORM_TEX(v.texcoord,_DissolveMap);
				o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				//采样噪声纹理
				fixed3 map = tex2D(_DissolveMap,i.uvMapTex).rgb;
				//阈值比较进行裁剪
				clip(map.r-_DissolveThreshold);
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 albedo = tex2D(_MainTex,i.uvMainTex).rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _Diffuse.rgb*_LightColor0.rgb*(dot(worldNormal,worldLight)*0.5+0.5);

				return fixed4(diffuse,1);
			}

			ENDCG
		}

		//负责处理阴影(避免以消融的片元产生阴影进而穿帮)
		Pass
		{
			Tags{"LightMode" = "ShadowCaster"}

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

			#pragma multi_compile_shadowcaster

			float _DissolveThreshold;
			sampler2D _DissolveMap;
			float4 _DissolveMap_ST;

			struct v2f
			{
				V2F_SHADOW_CASTER;
				float2	uvDissolveMap : TEXCOORD0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;

				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);

				o.uvDissolveMap = TRANSFORM_TEX(v.texcoord,_DissolveMap);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 map = tex2D(_DissolveMap,i.uvDissolveMap).rgb;

				clip(map.r-_DissolveThreshold);

				SHADOW_CASTER_FRAGMENT(i);
			}

			ENDCG
		}
	}

	FallBack"Diffuse"
}

在这里插入图片描述

从运行效果图可以看出消融效果比较突兀,没有过渡的感觉。例如像物体燃烧时逐渐碳化然后消融的效果无法达到。

渐变消融效果

基本思路:定义一个边界宽度来控制渐变的范围,通过smoothstep函数根据噪声图采样结果与阈值的相减值计算渐变系数,根据渐变系数通过插值函数得到渐变色,将渐变色与片元颜色进行混合输出最终表面颜色值。

Shader"MyShader/GradientDissolve"
{
	Properties
	{
		_Diffuse("Diffuse Color",Color) = (1,1,1,1)						//主颜色
		_MainTex("Main Tex",2D) = "white"{}								//主纹理
		_DissolveMap("Dissolve Map",2D) = "white"{}						//消融噪声
		_DissolveThreshold("Dissolve Threshold",Range(0.0,1.0)) = 0.0	//阈值
		_DissolveEdge("Dissolve Edge Width",Range(0.0,0.2)) = 0.1		//渐变边界宽度
		_DissolveColorA("Dissolve Color A",Color) = (1,1,1,1)			//边界颜色(近)
		_DissolveColorB("Dissolve Color B",Color) = (1,1,1,1)			//边界颜色(远)
	}

	SubShader
	{
		Tags
		{
			"RenderType" = "Opaque"
			"Queue" = "Geometry"
		}

		//主Pass负责消融效果
		Pass
		{
			Tags{"LightMode"="ForwardBase"}

			//关闭背面剔除同时渲染模型正面与背面(避免消融裸露模型内部)
			Cull Off

			CGPROGRAM
			#include"Lighting.cginc"
			#include"AutoLight.cginc"

			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _DissolveMap;
			float4 _DissolveMap_ST;
			float _DissolveThreshold;
			float _DissolveEdge;
			fixed4 _DissolveColorA;
			fixed4 _DissolveColorB;

			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uvMainTex : TEXCOORD0;
				float2 uvMapTex : TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
			};

			v2f vert(a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uvMainTex = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uvMapTex = TRANSFORM_TEX(v.texcoord,_DissolveMap);
				o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 map = tex2D(_DissolveMap,i.uvMapTex).rgb;
				
				clip(map.r-_DissolveThreshold);
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 albedo = tex2D(_MainTex,i.uvMainTex).rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _Diffuse.rgb*_LightColor0.rgb*(dot(worldNormal,worldLight)*0.5+0.5);

				
				/*
				smoothstep(edge0,edge1,x)函数:
				当x<=edge0时:返回0
				当edge0<=x<=edge1时:返回edge0与edge1间平滑插值
				当x>=edge0时:返回1
				*/
				//计算渐变系数
				fixed t = 1-smoothstep(0.0,_DissolveEdge,map.r-_DissolveThreshold);
				//计算渐变颜色
				fixed3 burnColor = lerp(_DissolveColorA,_DissolveColorB,t);
				burnColor = pow(burnColor,5);

				//颜色混合
				fixed3 finalColor = lerp(ambient + diffuse,burnColor,t*step(0.0001,_DissolveThreshold));
				return fixed4(finalColor,1);
			}

			ENDCG
		}

		//负责处理阴影
		Pass
		{
			Tags{"LightMode" = "ShadowCaster"}

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

			#pragma multi_compile_shadowcaster

			float _DissolveThreshold;
			sampler2D _DissolveMap;
			float4 _DissolveMap_ST;

			struct v2f
			{
				V2F_SHADOW_CASTER;
				float2	uvDissolveMap : TEXCOORD0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;

				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);

				o.uvDissolveMap = TRANSFORM_TEX(v.texcoord,_DissolveMap);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 map = tex2D(_DissolveMap,i.uvDissolveMap).rgb;

				clip(map.r-_DissolveThreshold);

				SHADOW_CASTER_FRAGMENT(i);
			}

			ENDCG
		}
	}

	FallBack"Diffuse"
}

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值