Unity Shader学习:Dissolve消融效果

Unity Shader学习:Dissolve消融效果

消融效果在游戏里非常常用,这里简单的实现下,代码里用到了if分支在shader里可能会费一点,如果想直接用puppet_master大佬版本的话可以自己改一下,主要if分支比较好理解,大佬的写法比较绕但是没用到分支,性能可能更好。

消融效果基本原理:利用噪声图的随机使主贴图在相应的位置逐渐剔除噪声颜色值小于一定阈值的像素,再在更靠内(噪声颜色值比阈值稍大一点)的地方安排一个内边缘,在内外边缘之间显示渐变的消融颜色。

shader部分:

Shader "Custom/DissolveEffect" {
	Properties {
	    _MainTex("MainTex",2D)="white"{}
	    _NoiseTex("NoiseTex",2D)="white"{}
		//高光大小
		_Gloss("Gloss",Range(0.0,20.0)) = 20.0
		//高光强度
		_SpecularFactor("SpecularFactor",Range(0.0,5.0)) = 1.0
		//消融阈值
		_DissolveThreshold("DissolveThreshold",Range(0.0,1.0)) = 0.5
		//消融系数(消融边界值=消融系数*消融阈值)
		_HalfDissolveThresholdFactor("HalfDissolveThresholdFactor",Range(1.0,2.0)) = 1.0
		//外侧颜色
		_DissolveEdgeColor("DissolveEdgeColor",Color) = (0,0,0,1)
		//内侧颜色
		_DissolveColor("DissolveColor",Color) = (1,0,0,1)
	}

	CGINCLUDE
        #include "Lighting.cginc"
        #include "UnityCG.cginc"
		sampler2D _MainTex;
	    sampler2D _NoiseTex;
		float _Gloss;
		float _SpecularFactor;
		float _DissolveThreshold;
		float _HalfDissolveThresholdFactor;
		float4 _DissolveEdgeColor;
		float4 _DissolveColor;

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

		v2f vert(appdata_full v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
			o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			return o;
		}

		float4 frag(v2f i) :SV_Target{
			//直接剔除小于阈值(越黑)的像素
			float4 noiseTexValue = tex2D(_NoiseTex, i.uv);
			if (noiseTexValue.r<_DissolveThreshold)
			{
				discard;
			}

			//漫反射
			float3 worldNormal = normalize(i.worldNormal);
			float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
			float3 lambert = max(0.0,dot(worldNormal, worldLightDir));
			float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
			float3 diffuseColor = lambert *_LightColor0.xyz+ambient;
			diffuseColor = diffuseColor * tex2D(_MainTex, i.uv).rgb;
			//高光
			float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
			float3 halfDir = normalize(worldLightDir + viewDir);
			float3 specular = _LightColor0.xyz*pow(saturate(dot(halfDir, worldNormal)), _Gloss);
			float3 finalColor = diffuseColor + specular*_SpecularFactor;
			//消融边界区域的最小值
			float halfDissolveThreshold = _DissolveThreshold * _HalfDissolveThresholdFactor;
			//噪声像素大于消融阈值但小于消融边界值,渐变颜色
			if (noiseTexValue.r<halfDissolveThreshold)
			{
				//得到噪声像素在两个边界间的权重
				float colorFactor = smoothstep(_DissolveThreshold, halfDissolveThreshold, noiseTexValue.r);
				//根据权重渐变颜色
				float3 finalColor = lerp(_DissolveColor, _DissolveEdgeColor, colorFactor);
				return float4(finalColor, 1);
			}
			else
			{
				//噪声像素大于消融边界值,返回漫反射
				return float4(finalColor, 1);
			}

			/*
			//用的话需要自行改动部分变量
			//大佬的优化版本,尽量不在shader中用分支判断的版本,但是代码很难理解啊....
			float percentage = _DissolveThreshold / dissolveValue.r;
			//如果当前百分比 - 颜色权重 - 边缘颜色
			float lerpEdge = sign(percentage - _ColorFactor - halfDissolveThreshold);
			//貌似sign返回的值还得saturate一下,否则是一个很奇怪的值
			fixed3 edgeColor = lerp(_DissolveEdgeColor.rgb, _DissolveColor.rgb, saturate(lerpEdge));
			//最终输出颜色的lerp值
			float lerpOut = sign(percentage - _ColorFactor);
			//最终颜色在原颜色和上一步计算的颜色之间差值(其实经过saturate(sign(..))的lerpOut应该只能是0或1)
			fixed3 colorOut = lerp(color, edgeColor, saturate(lerpOut));
			return fixed4(colorOut, 1);
			*/
		}

	ENDCG

	SubShader {
		Tags{"RenderType"="Opaque"}
		Pass{
		CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
		ENDCG
        }
	}
	FallBack "Diffuse"
}

你可能感兴趣的:(Unity,Shader)