Unity Shader 漫反射的實現
阿新 • • 發佈:2017-05-23
pos 獲取 函數實現 vertex 傳值 模型 dot ima unity
模型的漫反射可以在兩個函數中實現,一個是頂點函數,另外一個就是片元函數。而這兩個函數的區別又決定了漫反射實現出來的效果,那就是精細度。
因為頂點函數是逐頂點調用,漫反射在頂點函數實現時,對於在一個三角面(三個頂點包含的面)中的像素值是通過插值得到的。所以模型顯示的每個像素不是最細化的。
而片元函數是逐像素調用的,若漫反射在片元函數中調用,則會仔細涉及到每個像素,漫反射出來的效果也會更好一些。
下面我就直接放兩種Shader代碼的實現了。在這裏我在片元函數的漫反射直接使用了半蘭伯特光照模型。
頂點函數的漫反射(蘭伯特光照模型)
1 // 頂點函數漫反射的編寫 2 // 蘭伯特光照模型 3 // Diffuse = 直射光顏色 * max(cos(反射光,法線),0)Diffuse Vertex4 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 }
片元函數的漫反射(半蘭伯特光照模型)
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 漫反射的實現