[Unity Shader] 逐頂點光照和逐片元光照
書中的6.4節講的是漫反射的逐頂點光照和逐片元光照。
前一種算法是根據漫反射公式計算頂點顏色(頂點著色器),對顏色插值(光柵化過程)返回每個像素的顏色值(片元著色器)。
第二種算法是獲得頂點的法線(頂點著色器),對法線插值(光柵化過程),根據漫反射公式計算像素顏色(片元著色器)。
註:漫反射公式:(光源顏色 * 材質漫反射顏色)* (表面法線矢量 · 表面到光源的矢量)
書中對上兩種算法的實現如下:
1 Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex Level" 2 { 3 Properties逐頂點光照4 { 5 _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) 6 } 7 SubShader 8 { 9 Pass 10 { 11 Tags { "LightMode"="ForwardBase" } 12 13 CGPROGRAM 14 15 #pragma vertex vert 16 #pragma fragment frag 17 18#include "Lighting.cginc" 19 20 fixed4 _Diffuse; 21 22 struct a2v 23 { 24 float4 vertex : POSITION; 25 float3 normal : NORMAL; 26 }; 27 28 struct v2f 29 { 30 float4 pos : SV_POSITION;31 fixed3 color : COLOR; 32 }; 33 34 v2f vert (a2v v) 35 { 36 v2f o; 37 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 38 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; 39 fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object)); 40 fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); 41 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight)); 42 o.color = ambient + diffuse; 43 44 return o; 45 } 46 47 fixed4 frag(v2f i) : SV_Target 48 { 49 return fixed4(i.color, 1.0); 50 } 51 52 ENDCG 53 } 54 } 55 56 Fallback "Diffuse" 57 }
1 Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel Level" 2 { 3 Properties 4 { 5 _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) 6 } 7 SubShader 8 { 9 Pass 10 { 11 Tags { "LightMode"="ForwardBase" } 12 13 CGPROGRAM 14 15 #pragma vertex vert 16 #pragma fragment frag 17 18 #include "Lighting.cginc" 19 20 fixed4 _Diffuse; 21 22 struct a2v 23 { 24 float4 vertex : POSITION; 25 float3 normal : NORMAL; 26 }; 27 28 struct v2f 29 { 30 float4 pos : SV_POSITION; 31 fixed3 worldNormal : TEXCOORD0; 32 }; 33 34 v2f vert (a2v v) 35 { 36 v2f o; 37 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 38 o.worldNormal = mul(v.normal, (float3x3)_World2Object); 39 return o; 40 } 41 42 fixed4 frag(v2f i) : SV_Target 43 { 44 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; 45 fixed3 worldNormal = normalize(i.worldNormal); 46 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); 47 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); 48 fixed3 color = ambient + diffuse; 49 return fixed4(color.rgb, 1.0); 50 } 51 52 ENDCG 53 } 54 } 55 56 Fallback "Diffuse" 57 }逐片元光照
剛開始看這兩段代碼有點懵,覺得並沒有什麽區別,不理解為什麽將漫反射的計算挪到片元著色器中就能區別上面兩種算法。
查了一些帖子了解到,頂點著色器是對每個頂點數據進行處理的,包括空間轉換和顏色計算等,並不是對每個像素進行處理。而片元著色器有時又叫做像素著色器,它是對每個片元處理一次。片元著色器得到的片元數據(像素數據),是在光柵化過程中對頂點數據進行插值得到的,並不只是頂點數據,
“屬性插值的方法可以得到片元的顏色、法向量、深度值、紋理坐標等。”(https://wenku.baidu.com/view/219ecc7daef8941ea66e0519.html?pn=50NaN),即在片元著色器中使用的法線數據,其實已經是插值後的,因此在片元著色器中計算漫反射時,使用的就是逐片元光照的算法。這也是為什麽逐片元光照會比逐頂點光照效率低的原因(計算漫反射的次數更多)。
“但是,由於逐頂點光照依賴於線性插值來得到像素光照,因此,當光照模型中有非線性的計算(例如計算高光反射時)時,逐頂點光照就會出問題。而且,由於逐頂點光照會在渲染圖元內部對頂點顏色進行插值,這會導致渲染圖元內部的顏色總是暗於頂點處的最高顏色值,這在某些情況下會產生明顯的棱角現象。”
[Unity Shader] 逐頂點光照和逐片元光照