【Unity3d Shader】实现兰伯特与半兰伯特

先上效果图

改变光线角度得到2副图。

每幅图中的4副图分别为

左上:顶点着色器中兰伯特公式计算

右上:片元着色器中兰伯特公式计算

左下:顶点着色器中半兰伯特公式计算

右下:片元着色器中半兰伯特公式计算

 

   

下面讲原理

漫反射

平行光线射到凹凸不平的表面上,反射光线射向各个方向,这种反射叫做漫反射。

兰伯特定律(Lambert's law)

漫反射符合兰伯特定律:反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。

很明显,冬天没有夏天光线强的也是因为冬天的cosθx小于夏天的cosθ,

      冬天θ>夏天θ

=> 冬天cosθ<夏天cosθ

=> 冬天光线 < 夏天光线 

=> 冬冷夏热


公式如下:

Cdiffuse = (Clight*Mdiffuse)*max(0, N•L)

其中Cdiffuse代表漫反射后的颜色

Clight代表入射光的颜色

Mdiffuse代表材质颜色

NL都是单位向量

分析公式可看出θ>=90度 (N•L<=0)时 Cdiffuse的值<=0,那么背光面都为黑漆漆。

因此,提出了一个改良公式:半兰伯特光照模型

公式如下

Cdiffuse = (Clight*Mdiffuse)*((0, N•L) * 0.5 + 0.5)

只有完全背光面(N•L=-1)Cdiffuse 才会为0

 

核心代码如下:

fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * saturate(dot(worldNormal, worldLight));
fixed3 diffuse = _LightColor0.rgb * (dot(worldNormal, worldLight) * 0.5 + 0.5);
o.color = diffuse;

shader代码如下:

Shader "mgo/study/lambert_vertex"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		LOD 100

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

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

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

			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * saturate(dot(worldNormal, worldLight));
				o.color = diffuse;
				//fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//o.color = o.color + ambient;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 color = tex2D(_MainTex, i.uv);
				color.rgb = color.rgb * i.color;
				return color;
			}
			ENDCG
		}
	}
}
Shader "mgo/study/lambert_fragment"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		LOD 100

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

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

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

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

				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * saturate(dot(worldNormal, worldLight));
				color.rgb *= diffuse;
				//fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//color.rgb = color.rgb + ambient;
				return color;
			}
			ENDCG
		}
	}
}
Shader "mgo/study/half_lambert_vertex"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		LOD 100

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

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

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

			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * (dot(worldNormal, worldLight) * 0.5 + 0.5);
				o.color = diffuse;
				//fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//o.color = o.color + ambient;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 color = tex2D(_MainTex, i.uv);
				color.rgb = color.rgb * i.color;
				return color;
			}
			ENDCG
		}
	}
}
Shader "mgo/study/half_lambert_fragment"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		LOD 100

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

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

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

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

				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * (dot(worldNormal, worldLight) * 0.5 + 0.5);
				color.rgb *= diffuse;
				//fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//color.rgb = color.rgb + ambient;
				return color;
			}
			ENDCG
		}
	}
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值