Unity3D基礎篇----Shader學習筆記(3)


    

Unity3D基礎篇----Shader學習筆記(3)

193人閱讀 評論(0) 收藏 舉報
本文章已收錄於:
分類:

這一篇,我們來系統的介紹一下關於Unity的光照模型。

我們先來看看我們這一篇所實現的效果:


分別實現了半Lambert光照模型、Phong氏光照模型以及Blinn-Phong光照模型。

在講光照模型之前,我們先來對上一篇中關於蘭伯特光照模型的補充。

這裏先給出上節的實現效果圖:


不管是逐頂點還是逐像素的蘭伯特光照模型,向光面雖然是亮的,但是背光面有些是暗得看不見了。往往這不是我們需要的,我們通常看到暗部的時候,還是依稀能夠看清物體的。這個原因是因爲計算Lambert值時限定了它的值,即根據光的方向和物體表面法線進行點積,因爲都爲方向向量,所以主要取決於點積中兩向量的cos值,而cos的取值範圍爲[-1,1]之間,而我們在取蘭伯特值是使用了max來限定該值的,小於0則使用0:

  1. float Lambert = max(dot(normalDir, lightDir), 0);//蘭伯特值  
float Lambert = max(dot(normalDir, lightDir), 0);//蘭伯特值

所以,當兩個角度大於有180°的時候便沒有值了,所以物體顯現爲黑色。

這裏,我們需要對它進行改進,我們通過使用這麼一條公式來修改:

  1. value = a*cos(角度)+b  
value = a*cos(角度)+b
我們對cos的值進行a倍縮放,然後再b偏移。一般,它們的值均爲0.5.

所以,我們修改後的Lambert值如下:

  1. float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值  
float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值

其它屬性不變,這樣,就可以得到一個比較亮的物體了,而且暗部基本還是能夠看得清。

關於半Lambert的代碼實現如下:

  1. //Shader模塊定義  
  2. Shader "xiaolezi/Half Lambert Lighting Model Shader"  
  3. {  
  4.     //屬性設置  
  5.     Properties  
  6.     {  
  7.         //定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]  
  8.         _DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)  
  9.     }  
  10.     //第一個SubShader塊  
  11.     SubShader  
  12.         {  
  13.             //第一個Pass塊  
  14.             Pass  
  15.             {  
  16.                 //指定燈光渲染模式  
  17.                 Tags{ "LightMode" = "ForwardBase" }  
  18.   
  19.                 //開啓CG着色器編輯模塊  
  20.                 CGPROGRAM  
  21.                 //定義頂點着手器函數名  
  22.                 #pragma vertex vert  
  23.                 //定義片段着色器函數名  
  24.                 #pragma fragment frag  
  25.   
  26.                 //包含相關頭文件  
  27.                 #include "UnityCG.cginc"      
  28.                 #include "Lighting.cginc"  
  29.   
  30.                 //定義一個從應用程序到頂點數據的結構體  
  31.                 struct appdata  
  32.                 {  
  33.                     float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據  
  34.                     float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線  
  35.                 };  
  36.                 //定義一個從頂點數據到片段數據的結構體  
  37.                 struct v2f  
  38.                 {  
  39.                     float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據  
  40.                     float3 normal : COLOR0;//COLOR0語義:定義法線變量  
  41.                     float4 vertex : COLOR1;//COLOR1語義:定義頂點變量  
  42.                 };  
  43.                 //從屬性模塊中取得該變量  
  44.                 fixed4 _DiffuseColor;  
  45.                 //頂點着色器函數實現  
  46.                 v2f vert(appdata v)  
  47.                 {  
  48.                     v2f o;  
  49.                     o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標  
  50.                     o.normal = v.normal;  
  51.                     o.vertex = v.vertex;  
  52.                     return o;  
  53.                 }  
  54.                 //片段着色器函數實現  
  55.                 fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色  
  56.                 {  
  57.                     fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));   //計算世界法線方向  
  58.                     fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));            //計算燈光方向  
  59.   
  60.                     float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值  
  61.                     fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;    //計算漫反色  
  62.                     return fixed4(diffuse, 1.0);  
  63.                 }  
  64.                 //結束CG着色器編輯模塊  
  65.                 ENDCG  
  66.             }  
  67.         }  
  68.         Fallback "Diffuse"//默認着色器  
  69. }  
//Shader模塊定義
Shader "xiaolezi/Half Lambert Lighting Model Shader"
{
	//屬性設置
	Properties
	{
		//定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]
		_DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)
	}
	//第一個SubShader塊
	SubShader
		{
			//第一個Pass塊
			Pass
			{
				//指定燈光渲染模式
				Tags{ "LightMode" = "ForwardBase" }

				//開啓CG着色器編輯模塊
				CGPROGRAM
				//定義頂點着手器函數名
				#pragma vertex vert
				//定義片段着色器函數名
				#pragma fragment frag

				//包含相關頭文件
				#include "UnityCG.cginc"	
				#include "Lighting.cginc"

				//定義一個從應用程序到頂點數據的結構體
				struct appdata
				{
					float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據
					float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線
				};
				//定義一個從頂點數據到片段數據的結構體
				struct v2f
				{
					float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據
					float3 normal : COLOR0;//COLOR0語義:定義法線變量
					float4 vertex : COLOR1;//COLOR1語義:定義頂點變量
				};
				//從屬性模塊中取得該變量
				fixed4 _DiffuseColor;
				//頂點着色器函數實現
				v2f vert(appdata v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標
					o.normal = v.normal;
					o.vertex = v.vertex;
					return o;
				}
				//片段着色器函數實現
				fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色
				{
					fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));	//計算世界法線方向
					fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));			//計算燈光方向

					float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值
					fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;	//計算漫反色
					return fixed4(diffuse, 1.0);
				}
				//結束CG着色器編輯模塊
				ENDCG
			}
		}
		Fallback "Diffuse"//默認着色器
}

好,現在進入我們的正題,Unity中的光照模型。關於光與物體間的影響關係以及相關知識點,這裏就不多講解,我們這裏就直接來說說滿足光照模型所需要具備的條件:

1.自發光:光線可以直接由光源直接進入攝像機,而不需要經過其他物體的反射,它會直接取自材質的自發光顏色。通常來說,物體的自發光會影響周圍物體,但是在沒有使用全局光照的情況下,自發光是不被考慮的;

2.環境光:我們知道,物體表面除了受直接光照影響之外,周圍物體對光照的反射或散射也會對物體產生的影響,爲了模擬這一部分影響,我們直接使用Unity內置的環境光變量UNITY_LIGHTMODEL_AMBIENT來直接模擬環境光顏色,在Unity編輯器中,環境光的設置在菜單Windows->Lighting->Settings->Environment選項中可以進行相關設置;

3.漫反色:當光線從光源照射到物體模型表面時,會散射相對應幅度值,所以這裏的漫反色計算便是上一篇中我們實現的Lambert光照模型。

4.高光反射:當光線從光源照射到物體模型表面時,該表面會在完全鏡面反射方向散射多少幅度值。該值的計算我們使用這麼一個公式:

最終高光值 = 燈光顏色 * 材質高光顏色 * 高光模型值^材質光澤度

其中,材質高光顏色用於控制該材質對於高光的強度和顏色。材質光澤度用於控制高光區域的範圍大小,該值越大,範圍越小。而高光模型值的計算有如下兩種方式:

Phong氏高光值:攝像機的觀察方向與光照方向在物體模型法線的反射向量方向的點積。

Blinn-Phong高光值:物體表面模型法線與攝像機方向和燈光方向的角平分線的點積。


現在來看看具體的實現,我們先來看看Phong氏光照模型的實現:

  1. //Shader模塊定義  
  2. Shader "xiaolezi/Phong Lighting Model Shader"  
  3. {  
  4.     //屬性設置  
  5.     Properties  
  6.     {  
  7.         //定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]  
  8.         _DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)  
  9.         _Glossness("Glossness", Range(8, 256)) = 20<span style="white-space: pre;"> </span>//物體光澤度  
  10.         _SpecularColor("Specular Color", Color) = (1, 1, 1, 1)<span style="white-space: pre;">  </span>//高光顏色  
  11.     }  
  12.     //第一個SubShader塊  
  13.     SubShader  
  14.         {  
  15.             //第一個Pass塊  
  16.             Pass  
  17.             {  
  18.                 //指定燈光渲染模式  
  19.                 Tags{ "LightMode" = "ForwardBase" }  
  20.   
  21.                 //開啓CG着色器編輯模塊  
  22.                 CGPROGRAM  
  23.                 //定義頂點着手器函數名  
  24.                 #pragma vertex vert  
  25.                 //定義片段着色器函數名  
  26.                 #pragma fragment frag  
  27.   
  28.                 //包含相關頭文件  
  29.                 #include "UnityCG.cginc"      
  30.                 #include "Lighting.cginc"  
  31.   
  32.                 //定義一個從應用程序到頂點數據的結構體  
  33.                 struct appdata  
  34.                 {  
  35.                     float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據  
  36.                     float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線  
  37.                 };  
  38.                 //定義一個從頂點數據到片段數據的結構體  
  39.                 struct v2f  
  40.                 {  
  41.                     float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據  
  42.                     float3 normal : COLOR0;//COLOR0語義:定義法線變量  
  43.                     float4 vertex : COLOR1;//COLOR1語義:定義頂點變量  
  44.                 };  
  45.                 //從屬性模塊中取得該變量  
  46.                 fixed4 _DiffuseColor;  
  47.                 float _Glossness;  
  48.                 fixed4 _SpecularColor;  
  49.   
  50.                 //頂點着色器函數實現  
  51.                 v2f vert(appdata v)  
  52.                 {  
  53.                     v2f o;  
  54.                     o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標  
  55.                     o.normal = v.normal;  
  56.                     o.vertex = v.vertex;  
  57.                     return o;  
  58.                 }  
  59.                 //片段着色器函數實現  
  60.                 fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色  
  61.                 {  
  62.                     fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));   //計算世界法線方向  
  63.                     fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));            //計算燈光方向  
  64.                     fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向  
  65.   
  66.                     //環境光  
  67.                     fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;  
  68.                     //漫反色  
  69.                     float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值  
  70.                     fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;    //計算漫反色  
  71.                     //高光  
  72.                     fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));//根據物體表面法線計算光的反射光方向  
  73.                     fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(reflectDir, viewDir)), _Glossness);//Phong氏高光計算  
  74.                       
  75.                     return fixed4(ambient + diffuse + specular, 1.0);  
  76.                 }  
  77.                     //結束CG着色器編輯模塊  
  78.                 ENDCG  
  79.             }  
  80.         }  
  81.   
  82.         Fallback "Specular"//默認着色器,這裏選擇高光  
  83. }  
//Shader模塊定義
Shader "xiaolezi/Phong Lighting Model Shader"
{
	//屬性設置
	Properties
	{
		//定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]
		_DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)
		_Glossness("Glossness", Range(8, 256)) = 20	//物體光澤度
		_SpecularColor("Specular Color", Color) = (1, 1, 1, 1)	//高光顏色
	}
	//第一個SubShader塊
	SubShader
		{
			//第一個Pass塊
			Pass
			{
				//指定燈光渲染模式
				Tags{ "LightMode" = "ForwardBase" }

				//開啓CG着色器編輯模塊
				CGPROGRAM
				//定義頂點着手器函數名
				#pragma vertex vert
				//定義片段着色器函數名
				#pragma fragment frag

				//包含相關頭文件
				#include "UnityCG.cginc"	
				#include "Lighting.cginc"

				//定義一個從應用程序到頂點數據的結構體
				struct appdata
				{
					float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據
					float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線
				};
				//定義一個從頂點數據到片段數據的結構體
				struct v2f
				{
					float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據
					float3 normal : COLOR0;//COLOR0語義:定義法線變量
					float4 vertex : COLOR1;//COLOR1語義:定義頂點變量
				};
				//從屬性模塊中取得該變量
				fixed4 _DiffuseColor;
				float _Glossness;
				fixed4 _SpecularColor;

				//頂點着色器函數實現
				v2f vert(appdata v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標
					o.normal = v.normal;
					o.vertex = v.vertex;
					return o;
				}
				//片段着色器函數實現
				fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色
				{
					fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));	//計算世界法線方向
					fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));			//計算燈光方向
					fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向

					//環境光
					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
					//漫反色
					float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值
					fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;	//計算漫反色
					//高光
					fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));//根據物體表面法線計算光的反射光方向
					fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(reflectDir, viewDir)), _Glossness);//Phong氏高光計算
					
					return fixed4(ambient + diffuse + specular, 1.0);
				}
					//結束CG着色器編輯模塊
				ENDCG
			}
		}

		Fallback "Specular"//默認着色器,這裏選擇高光
}
我們直接看到片元着色器函數的實現。

首先我們先定義了需要計算的向量方向,我們都通過模型座標來轉換爲相對應的向量方向。漫反色需要使用的是法線和燈光向量方向,而高光需要使用法線、燈光以及攝像機向量方向:

  1. fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));   //計算世界法線方向  
  2. fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));            //計算燈光方向  
  3. fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向  
fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));	//計算世界法線方向
fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));			//計算燈光方向
fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向

環境光直接使用內置變量來取值:

  1. //環境光  
  2. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;  
//環境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;


漫反色我們使用半蘭伯特光照模型來實現:

  1. //漫反色  
  2. float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值  
  3. fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;    //計算漫反色  
//漫反色
float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;	//計算漫反色


比較複雜的就是高光的計算:

  1. //高光  
  2. fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));//根據物體表面法線計算光的反射光方向  
  3. fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(reflectDir, viewDir)), _Glossness);//Phong氏高光計算  
//高光
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));//根據物體表面法線計算光的反射光方向
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(reflectDir, viewDir)), _Glossness);//Phong氏高光計算
首先我們通過reflect函數求出入射光根據物體表面法線所反射的向量,然後再通過該值求出高光模型值之後與燈光和材質高光顏色進行相乘得到最終高光顏色。

最後,再對所計算的顏色值進行疊加得到最終顏色值並返回輸出到屏幕上:

  1. return fixed4(ambient + diffuse + specular, 1.0);  
return fixed4(ambient + diffuse + specular, 1.0);


如果明白了Phong氏高光模型,Blinn-Phong高光模型自然也就很容易懂:

  1. //Shader模塊定義  
  2. Shader "xiaolezi/Blinn Phong Lighting Model Shader"  
  3. {  
  4.     //屬性設置  
  5.     Properties  
  6.     {  
  7.         //定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]  
  8.         _DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)  
  9.         _Glossness("Glossness", Range(8, 256)) = 20  
  10.         _SpecularColor("Specular Color", Color) = (1, 1, 1, 1)  
  11.     }  
  12.     //第一個SubShader塊  
  13.     SubShader  
  14.         {  
  15.             //第一個Pass塊  
  16.             Pass  
  17.             {  
  18.                 //指定燈光渲染模式  
  19.                 Tags{ "LightMode" = "ForwardBase" }  
  20.   
  21.                 //開啓CG着色器編輯模塊  
  22.                 CGPROGRAM  
  23.                 //定義頂點着手器函數名  
  24.                 #pragma vertex vert  
  25.                 //定義片段着色器函數名  
  26.                 #pragma fragment frag  
  27.   
  28.                 //包含相關頭文件  
  29.                 #include "UnityCG.cginc"      
  30.                 #include "Lighting.cginc"  
  31.   
  32.                 //定義一個從應用程序到頂點數據的結構體  
  33.                 struct appdata  
  34.                 {  
  35.                     float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據  
  36.                     float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線  
  37.                 };  
  38.                 //定義一個從頂點數據到片段數據的結構體  
  39.                 struct v2f  
  40.                 {  
  41.                     float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據  
  42.                     float3 normal : COLOR0;//COLOR0語義:定義法線變量  
  43.                     float4 vertex : COLOR1;//COLOR1語義:定義頂點變量  
  44.                 };  
  45.                 //從屬性模塊中取得該變量  
  46.                 fixed4 _DiffuseColor;  
  47.                 float _Glossness;  
  48.                 fixed4 _SpecularColor;  
  49.   
  50.                 //頂點着色器函數實現  
  51.                 v2f vert(appdata v)  
  52.                 {  
  53.                     v2f o;  
  54.                     o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標  
  55.                     o.normal = v.normal;  
  56.                     o.vertex = v.vertex;  
  57.                     return o;  
  58.                 }  
  59.                 //片段着色器函數實現  
  60.                 fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色  
  61.                 {  
  62.                     fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));   //計算世界法線方向  
  63.                     fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));            //計算燈光方向  
  64.                     fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向  
  65.   
  66.                     //環境光  
  67.                     fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;  
  68.                     //漫反色  
  69.                     float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值  
  70.                     fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;    //計算漫反色  
  71.                     //高光  
  72.                     fixed3 halfDir = normalize(lightDir + viewDir);//根據物體表面法線計算光的反射光方向  
  73.                     fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(halfDir, normalDir)), _Glossness);//Phong氏高光計算  
  74.                       
  75.                     return fixed4(ambient + diffuse + specular, 1.0);  
  76.                 }  
  77.                     //結束CG着色器編輯模塊  
  78.                 ENDCG  
  79.             }  
  80.         }  
  81.   
  82.         Fallback "Specular"//默認着色器,這裏選擇高光  
  83. }  
//Shader模塊定義
Shader "xiaolezi/Blinn Phong Lighting Model Shader"
{
	//屬性設置
	Properties
	{
		//定義一個物體表面顏色,格式:[屬性名]([Inspector面板顯示名字],屬性類型)=[初始值]
		_DiffuseColor("Diffuse Color", Color) = (1, 1, 1, 1)
		_Glossness("Glossness", Range(8, 256)) = 20
		_SpecularColor("Specular Color", Color) = (1, 1, 1, 1)
	}
	//第一個SubShader塊
	SubShader
		{
			//第一個Pass塊
			Pass
			{
				//指定燈光渲染模式
				Tags{ "LightMode" = "ForwardBase" }

				//開啓CG着色器編輯模塊
				CGPROGRAM
				//定義頂點着手器函數名
				#pragma vertex vert
				//定義片段着色器函數名
				#pragma fragment frag

				//包含相關頭文件
				#include "UnityCG.cginc"	
				#include "Lighting.cginc"

				//定義一個從應用程序到頂點數據的結構體
				struct appdata
				{
					float4 vertex : POSITION;//POSITION語義:表示從該模型中獲取到頂點數據
					float3 normal : NORMAL;  //NORMAL語義:獲取該模型法線
				};
				//定義一個從頂點數據到片段數據的結構體
				struct v2f
				{
					float4 pos : SV_POSITION;//SV_POSITION語義:從頂點輸出數據中獲取到頂點數據
					float3 normal : COLOR0;//COLOR0語義:定義法線變量
					float4 vertex : COLOR1;//COLOR1語義:定義頂點變量
				};
				//從屬性模塊中取得該變量
				fixed4 _DiffuseColor;
				float _Glossness;
				fixed4 _SpecularColor;

				//頂點着色器函數實現
				v2f vert(appdata v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);//讓模型頂點數據座標從本地座標轉化爲屏幕剪裁座標
					o.normal = v.normal;
					o.vertex = v.vertex;
					return o;
				}
				//片段着色器函數實現
				fixed4 frag(v2f f) : SV_Target//SV_Target語義:輸出片元着色器值,可直接認爲是輸出到屏幕顏色
				{
					fixed3 normalDir = normalize(UnityObjectToWorldNormal(f.normal));	//計算世界法線方向
					fixed3 lightDir = normalize(ObjSpaceLightDir(f.vertex));			//計算燈光方向
					fixed3 viewDir = normalize(ObjSpaceViewDir(f.vertex));//計算觀察方向

					//環境光
					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
					//漫反色
					float Lambert = 0.5 * dot(normalDir, lightDir) + 0.5;//蘭伯特值
					fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * Lambert;	//計算漫反色
					//高光
					fixed3 halfDir = normalize(lightDir + viewDir);//根據物體表面法線計算光的反射光方向
					fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(halfDir, normalDir)), _Glossness);//Phong氏高光計算
					
					return fixed4(ambient + diffuse + specular, 1.0);
				}
					//結束CG着色器編輯模塊
				ENDCG
			}
		}

		Fallback "Specular"//默認着色器,這裏選擇高光
}

主要區別便是高光的計算不同:

  1. //高光  
  2. fixed3 halfDir = normalize(lightDir + viewDir);//根據物體表面法線計算光的反射光方向  
  3. fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(halfDir, normalDir)), _Glossness);//Phong氏高光計算  
//高光
fixed3 halfDir = normalize(lightDir + viewDir);//根據物體表面法線計算光的反射光方向
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(halfDir, normalDir)), _Glossness);//Phong氏高光計算
具體的就不再多講了。

關於這兩個模型的高光計算,其實Blinn-Phong會相對Phong光照模型會好很多,比如上面使用reflect函數來計算反射向量,當中涉及的計算相對於這個求角平分線的要複雜很多,所以性能自然也會下降很多。就效果而言,Blinn-Phong光照模型會比Phong光照模型亮很多。所以,Blinn-Phong光照模型也是對Phong氏光照模型的一種拓展優化。


以上便是對Unity中光照模型進行簡單的介紹以及實現,希望能夠對讀者有所啓發。

代碼倉庫也已經更新,有需要的可以進入鏈接下載頁面進行克隆下載:GitHub倉庫地址

Happy Coding...

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