【Unity Shader】简单积雪效果的实现

1.前言

公司的项目进入真机调试阶段,体验了一个月的996模式正式结束,放假第一天来写篇博客

 2.实现思路

1.积雪的实现

一般由模型的纹理贴图和一张积雪的纹理图混合而成。

//2个采样结果的差值(1.模型纹理,2.积雪纹理/颜色)
color.rgb = lerp(color, _SnowColor, SnowThreshold);

不过因为没找到比较合适的积雪纹理图,作为一名实习生也不好意思让公司的美术大佬帮我量身定做,所以退而求其实选择使用颜色-白色作为纹理的替代。

_SnowColor ("Snow Color", Color) = (1.0, 1.0, 1.0, 1.0)

以世界法线和垂直方向夹角点积作为积雪厚度判断标准,积雪的程度由参数SnowLevel,SnowDepth来共同控制。

float _SnowLevel;
float _SnowDepth;

//计算阈值,以世界法线和垂直方向夹角点积作为积雪厚度判断标准,参数Snowlevel,SnowDepth一起控制积雪程度
//一般来说,夹角越小(点积值越大)积雪越厚
half SnowThreshold = dot(i.worldNormal, float3(0, 1, 0)) - lerp(1, -1, _SnowLevel);
SnowThreshold = saturate(SnowThreshold / _SnowDepth);

2.光照

还是使用Lambert漫反射光照模型,上次没介绍,这里简单介绍一下公式吧

C_{LambertDiffuse} = (C_{Light} \cdot M_{Diffuse})Max(0,\widehat{n}\cdot \widehat{l})

其中:

 C_{Light} 光源颜色

M_{Diffuse} 材质漫反射颜色

Max(0,\widehat{n}\cdot \widehat{l})  法线与光源方向点积的正数值

 

 3.代码实现

以下是完整的shader代码

Shader "Unlit/SnowShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_SnowColor ("Snow Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_SnowLevel ("Snow Level", Range(0, 1)) = 0
		_SnowDepth ("Snow Depth", Range(0, 0.5)) = 0.1
	}
	
	SubShader{

		Tags {"RenderType" = "Opaque"}

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

		sampler2D _MainTex;
		float4 _MainTex_ST;
		float4 _SnowColor;
		float _SnowLevel;
		float _SnowDepth;
		
		struct a2v{
			float4 vertex : POSITION;
			float3 normal : NORMAL;
			float2 texcoord : TEXCOORD0;	
		};

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

		v2f vert(a2v v) {
			v2f o;
			
			o.pos = UnityObjectToClipPos(v.vertex);
			o.worldNormal = UnityObjectToWorldNormal(v.normal);
			o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
			o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			
			TRANSFER_SHADOW(o);
			return o;
		}

		fixed4 frag(v2f i) : SV_Target{

			//使用Lambert光照模型

			//材质漫反射颜色
			fixed3 albedo = tex2D(_MainTex, i.uv).rgb;
			//入射光方向
			fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
			//世界法线
			fixed3 worldNormal = normalize(i.worldNormal);
			//环境光
			fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
			//漫反射光照
			fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, -worldLightDir));
			//统一光照衰减和阴影
			UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
			//混合光照
			fixed3 lightColor = diffuse * atten + ambient;
			//纹理采样
			fixed4 color = tex2D(_MainTex, i.uv);

			//计算阈值,以世界法线和垂直方向夹角点积作为积雪厚度判断标准,参数Snowlevel,SnowDepth一起控制积雪程度
			//一般来说,夹角越小(点积值越大)积雪越厚
			half SnowThreshold = dot(i.worldNormal, float3(0, 1, 0)) - lerp(1, -1, _SnowLevel);
			SnowThreshold = saturate(SnowThreshold / _SnowDepth);
			// SnowThreshold = saturate(_SnowDepth / SnowThreshold);
			color.rgb = lerp(color, _SnowColor, SnowThreshold);
			//混合颜色 输出,这里把插值函数第三个值置为1,可以得到一种类似卡通风格的渲染(也就是只有color作为输出)
			fixed3 finalColor = lerp(lightColor, color, SnowThreshold);

			return float4(finalColor, 1);

		}

		ENDCG


		Pass{

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			ENDCG

		}

	}

	FallBack "Diffuse"
}

4.实现效果

写在最后

项目链接:我的GitHub

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值