1. 程式人生 > >Unity Shader 漫反射的實現

Unity Shader 漫反射的實現

pos 獲取 函數實現 vertex 傳值 模型 dot ima unity

模型的漫反射可以在兩個函數中實現,一個是頂點函數,另外一個就是片元函數。而這兩個函數的區別又決定了漫反射實現出來的效果,那就是精細度。

因為頂點函數是逐頂點調用,漫反射在頂點函數實現時,對於在一個三角面(三個頂點包含的面)中的像素值是通過插值得到的。所以模型顯示的每個像素不是最細化的。

而片元函數是逐像素調用的,若漫反射在片元函數中調用,則會仔細涉及到每個像素,漫反射出來的效果也會更好一些。

下面我就直接放兩種Shader代碼的實現了。在這裏我在片元函數的漫反射直接使用了半蘭伯特光照模型。

頂點函數的漫反射(蘭伯特光照模型)

技術分享
 1 // 頂點函數漫反射的編寫
 2 // 蘭伯特光照模型
 3 // Diffuse = 直射光顏色 * max(cos(反射光,法線),0)
4 Shader "TMoon/02-Diffuse Vertex" { 5 Properties{ 6 _Diffuse("Diffuse Color",Color) = (1,1,1,1) 7 } 8 9 SubShader{ 10 11 Pass{ 12 13 Tags {"LightMode" = "ForwardBase"} 14 15 CGPROGRAM 16 17 // 類似C#的 using 引用類庫或者文件
18 // 這裏引用了Unity內置的光照的類庫 19 // 配合"LightMode" = "ForwarBase"得到Unity場景中的光照信息 20 #include "Lighting.cginc" 21 22 #pragma vertex vert 23 #pragma fragment frag 24 25 //獲得面板參數 26 fixed4 _Diffuse; 27 28 //
這裏使用第二種方法傳值 結構體 29 // application to vertex 30 // 由應用程序傳遞給頂點函數的參數 31 struct a2v { 32 float4 vertex : POSITION; //應用程序將模型的頂點坐標填充到vertex 33 float3 normal : NORMAL; //應用程序將模型的頂點法線填充到normal 34 }; 35 36 // vertex to fragment 37 // 由頂點函數傳遞給片元函數的參數 38 struct v2f { 39 float4 position : SV_POSITION; //模型裁剪空間下的頂點坐標 40 float3 color : COLOR0; //COLOR0,COLOR1...這些類型一般是中介,用於存儲和傳遞數據,有時並沒有什麽實際意義,就是中介而已。 41 }; 42 43 v2f vert(a2v v) { 44 v2f f; 45 // 將模型頂點坐標變換到裁剪空間的坐標並賦值給v2f.position 46 f.position = mul(UNITY_MATRIX_MVP, v.vertex); 47 48 // UNITY_LIGHTMODEL_AMBIENT用來獲取環境光 49 // 獲取Unity環境光的顏色值 50 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rag; 51 52 // 將模型頂點的法線轉換到空間坐標下,方便一致計算 53 // UnityObjectToWorldNormal 把法線方向 模型空間 ——> 世界空間 54 fixed3 normalDir = normalize(UnityObjectToWorldNormal(v.normal)); 55 56 // _WorldSpaceLightPos0 空間坐標下光的方向 57 // 對於每個頂點來說 光的位置就是光的方向 ,因為光是平行光 58 // 在這裏我直接理解為了反射光 59 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); 60 61 // 蘭伯特光照模型 62 // _LightColor0.rab 直射光顏色 63 fixed3 diffuse = _LightColor0.rab * max(dot(normalDir, lightDir),0) * _Diffuse.rgb; 64 65 // 將漫反射加上環境光的影響 66 f.color = diffuse + ambient; 67 68 return f; 69 } 70 71 float4 frag(v2f f) : SV_Target{ 72 return fixed4(f.color,1); 73 } 74 75 ENDCG 76 } 77 } 78 79 Fallback "VertexLit" 80 }
Diffuse Vertex

片元函數的漫反射(半蘭伯特光照模型)

技術分享
 1 // 片元函數漫反射的編寫  細節更多 邊緣過渡更順    可以對比頂點函數漫反射
 2 // 半蘭伯特光照模型  陰影部分不會全黑,如果全黑對玩家不太友好  可以對比蘭伯特光照模型  在02-Diffuse Vertex把環境光去掉即可明顯對比
 3 // Diffuse = 直射光顏色 * (cos(反射光,法線)*0.5+0.5)
 4 Shader "TMoon/03-Diffuse Fragment" {
 5     Properties{
 6         _Diffuse("Diffuse Color",Color) = (1,1,1,1)
 7     }
 8 
 9     SubShader{
10 
11         Pass{
12         
13             Tags {"LightMode" = "ForwardBase"}
14 
15             CGPROGRAM
16               
17             // 類似C#的 using 引用類庫或者文件
18             // 這裏引用了Unity內置的光照的類庫
19             // 配合"LightMode" = "ForwarBase"得到Unity場景中的光照信息
20             #include "Lighting.cginc"
21 
22             #pragma vertex vert
23             #pragma fragment frag
24             
25             //獲得面板參數
26             fixed4 _Diffuse;
27 
28             // application to vertex
29             // 由應用程序傳遞給頂點函數的參數
30             struct a2v {
31                 float4 vertex : POSITION; //應用程序將模型的頂點坐標填充到vertex
32                 float3 normal : NORMAL; //應用程序將模型的頂點法線填充到normal
33             };
34 
35             // vertex to fragment
36             // 由頂點函數傳遞給片元函數的參數
37             struct v2f {
38                 float4 position : SV_POSITION; //模型裁剪空間下的頂點坐標
39                 float3 worldNormalDir : COLOR0;    //COLOR0,COLOR1...這些類型一般是中介,用於存儲和傳遞數據,有時並沒有什麽實際意義,就是中介而已。
40             };
41 
42             v2f vert(a2v v) {
43                 v2f f;
44 
45                 // 將模型頂點坐標變換到裁剪空間的坐標並賦值給v2f.position
46                 f.position = mul(UNITY_MATRIX_MVP, v.vertex);
47 
48                 // 將模型頂點的法線轉換到空間坐標下,方便一致計算
49                 // UnityObjectToWorldNormal 把法線方向 模型空間 ——> 世界空間
50                 f.worldNormalDir = normalize(UnityObjectToWorldNormal(v.normal));
51 
52                 return f;
53             }
54 
55             float4 frag(v2f f) : SV_Target{
56 
57                 // 反射光
58                 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
59 
60                 // 半蘭伯特光照模型
61                 fixed3 diffuse = _LightColor0.rab * (dot(f.worldNormalDir, lightDir)*0.5+0.5) * _Diffuse.rgb;
62 
63                 return fixed4(diffuse,1);
64             }
65 
66             ENDCG
67         }
68     }
69 
70     Fallback "VertexLit"
71 }
Diffuse Fragment

這裏通過截圖顯示一下蘭伯特光照模型和半蘭伯特光照模型的區別。

技術分享

技術分享

通過背面就很明顯看得出了。這篇文章就到這裏了。

Unity Shader 漫反射的實現