Unity Shader - 基礎光照之漫反射

在shader中,光照是一個非常基礎並且重要的知識點,下面我們就來學習一下shader中的基礎光照。

在現實中的光照是極其複雜的,而且會受到諸多因素的影響,這是我們有限的計算能力所無法模擬的。因此我們在渲染中基本上都是使用一個簡化的模型來模擬真實的光照環境。來達到我們想要的效果,看起來差不多就行。

計算機圖形學第一定律:如果它看起來是對的,那麼它就是對的。

——————《3D數學基礎:圖形與遊戲開發》

標準光照模型

光照模型的主要結構由4個分量組成:

  • 自發光(emissive): 一個表面本身發射的光。
  • 環境光(ambient): 模擬其他所有的間接光照。
  • 漫反射(diffuse): 用於描述當光線從光源照射到模型表面時,該表面會向每個方向散射的輻射量。
  • 鏡面高光(specular): 用於描述當光線從光源照射到模型表面時,該表面會完全鏡面反射方向散射的輻射量。

1.自發光(emissive)

它的計算很簡單,就是直接使用了該材質的自發光顏色
cemissive=memissivec_{emissive}=m_{emissive}
通常在實時渲染中,自發光的表面往往並不會照亮周圍的表面,也就是說,這個物體並不會被當成一個光源。

在片元着色器中直接返回一個顏色值就ok

fixed4 frag (v2f i) : SV_Target
{
    return _Color;
}

如圖:
在這裏插入圖片描述

2.環境光(ambient)

光通常都不是來自同一個光源,而是來自於我們周圍分散的很多光源,由其他物體反射而來的光,爲了模擬這種間接光照,我們使用了一個常亮:環境光。

在Unity中可以通過 window => Rendering => Lighting Setting 修改環境光顏色。

shader中可以通過內置變量 UNITY_LIGHTMODEL_AMBIENT 獲取環境光。

fixed4 frag (v2f i) : SV_Target
{
	//環境光
	fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT.rgba;
    return _Color+ambient;
}

我們可以看到該物體會隨着環境光的改變而改變。
在這裏插入圖片描述

3.漫反射(diffuse)

漫反射(diffuse) 是光線照射在物體粗糙的表面會無序地向四周反射的現象。是投射在粗糙表面上的光向各個方向反射的現象。當一束平行的入射光線射到粗糙的表面時,表面會把光線向着四面八方反射,所以入射線雖然互相平行,由於各點的法線方向不一致,造成反射光線向不同的方向無規則地反射,

我們可以通過下圖觀察到,反射光的強度取決於在表面法向量和入射光的光線之間的角度的餘弦值。
在這裏插入圖片描述
所以我們首先需要計算出法向量N入射光方向L的角度的餘弦值。

我們可以通過他們的點乘來計算,公式如下:

LN=LNcosθL · N =|L||N| cos\theta

把向量歸一化處理後,|L| 和 |N| 都是1,所有可以簡化爲:

LN=cosθL · N =cos\theta

所以得出最終計算 漫反射的公式

Cdiffuse=ClightMlightMax(0,LN)C_{diffuse}=C_{light}\cdot M_{light} \cdot Max(0,L\cdot N)

  • ClightC_{light}:入射光顏色
  • MlightM_{light}:材質顏色
  • max函數:防止點乘結果爲負數
  • LL:入射方向
  • NN:當前點的法向量

演示:
在這裏插入圖片描述
Unity Shader代碼如下:

PS:這裏是在片元着色器裏計算的,也可以在頂點着色器裏計算。
如果想要效果好就在片元着色器裏計算。
如果想要性能好就在頂點着色器裏計算。

Shader "lcl/baseLighting/002_Diffuse_fargment" {
	//在片元着色器計算漫反射
	SubShader {
		Pass{
			Tags { "LightMode"="ForwardBase" }
			CGPROGRAM
			#include "Lighting.cginc"
			#pragma vertex vert
			#pragma fragment frag


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

			struct v2f{
				float4 position:SV_POSITION;
				float3 worldNormalDir:COLOR0;
			};

			v2f vert(a2v v){
				v2f f;
				f.position = UnityObjectToClipPos(v.vertex);
				f.worldNormalDir = mul(v.normal,(float3x3) unity_WorldToObject);
				return f;
			};


			fixed4 frag(v2f f):SV_TARGET{
				//環境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
				//法向量
				fixed3 normalDir = normalize(f.worldNormalDir);
				//光照方向
				fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
				//漫反射計算
				fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir),0);
				fixed3 resultColor = diffuse+ambient;
				return fixed4(resultColor,1);
			};

			ENDCG
		}
	}
	FallBack "VertexLit"
}

以上用到的光照模型是 蘭伯特光照模型(Lambert)

其實如果我們仔細觀察會發現背光的地方非常暗,完全就看不見該球體的形狀了,這樣的話效果就不太理想了,如圖:
在這裏插入圖片描述
有時候我們需要在背光的地方也能有一點光線,此時我們就可以用 半蘭伯特光照模型 了,該技術是Valve公式在開發遊戲《半條命》時提出的,由於該技術是在原蘭伯特光照模型的基礎上修改的,所以被稱爲半蘭伯特光照模型

半蘭伯特光照模型:

Cdiffuse=ClightMlight((LN)0.5+0.5)C_{diffuse}=C_{light}\cdot M_{light} \cdot ((L\cdot N) * 0.5+0.5)

其實就是把 LNL·N的結果範圍從 [-1,1] 映射到 [0,1]的範圍內。這樣的話在背光面也會有明暗變化,不會完全黑掉。

我們通過下圖可以清楚的看到他們的取值區間:
在這裏插入圖片描述
效果演示:
在這裏插入圖片描述
關鍵代碼如下:

fixed4 frag(v2f f):SV_TARGET{
	fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
	fixed3 normalDir = normalize(f.worldNormalDir);
	fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
	//半蘭伯特漫反射  值範圍0-1
	fixed3 halfLambert = dot(normalDir,lightDir)*0.5+0.5;	
	fixed3 diffuse = _LightColor0.rgb * halfLambert;
	fixed3 resultColor = diffuse+ambient;

	return fixed4(resultColor,1);
};

結束!下節繼續探索高光反射(Specular), Phone和BlinnPnone光照模型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章