這幾天一直在為了研究清楚 ShadowGun 示例的 shader,但沒寫過 Unity 的 shader,於是從頭開始閱讀官方的說明,發現多出了 SurfaceShader 的概念,再加上對 Unity 的光照系統不太了解,看起來的確實有點頭暈,細心看了看后還是有點頭緒。於是就把上一篇的討論過的法線貼圖實現一下吧,其實想在 Unity 里面使用法線貼圖效果,簡直簡單的像畫一個一字,直接選一個內建的 BumpMap 就好了,甚至你使用 SurfaceShader 直接讀取出法線就好了,其它也不用管。但是要是非要使用 vertex/fragment shader 一步一步的寫完法線貼圖的所有實現過程,就只能一步步來,最重要的當然就是要自己處理 tbn 矩陣。正好趁這個機會復習下前面的內容,寫這個的過程遇到了不少細節上的不明白,不過解決后就更明白了。
“Use Scene Light” 大於0時,使用的是場景里的 Light(你需要自己拖一個 Direction Light),如果小於等於0就是用下面的 “Light Dir”,自定義一個燈光位置,燈光的方向從世界坐標系的原點指向該位置 (LightDir = LightPos - OriginalPos)。這里面我已經加入了高光 Specular Light。
代碼如下:
Shader "Custom/ManuallyNormalMapping" { Properties { _MainTex("Main Tex", 2D) = "white" {} _BumpTex("Bump Tex", 2D) = "bump" {} _Ambient("Ambient Color", Color) = (0.6, 0.6, 0.6, 1.0) _Diffuse("Diffuse Color", Color) = (0.7, 0.7, 0.8, 1.0) _Specular("Specular Color", Color) = (1.0, 1.0, 1.0, 1.0) _UseSceneLight("Use Scene Light", Float) = 1.0 _LightDir("Light Dir", Vector) = (0.0, -1.0, 1.0, 0.0) } SubShader { Tags {"RenderType" = "Opaque"} Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // fragma input struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 viewDir : TEXCOORD1; float3 lightDir : TEXCOORD2; }; sampler2D _MainTex; sampler2D _BumpTex; float4 _Ambient; float4 _Diffuse; float4 _Specular; float _UseSceneLight; float4 _LightDir; v2f vert(appdata_full v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord; float3 tangent = normalize(v.tangent); float3 normal = normalize(v.normal); float3 binormal = normalize(cross(normal, v.tangent.xyz) * v.tangent.w); // All vector here is col vector, matrix is left mulpitlied. // So TBN matrix is [T, B, N], after normalized, TBN's inverse matrix is [T, B, N]T. float3x3 TBN; TBN[0] = tangent; TBN[1] = binormal; TBN[2] = normal; // We do not need this. //TBN = transpose(TBN); // Assume from view space. float3 viewDir = ObjSpaceViewDir(v.vertex); float3 lightDir = float3(0.0, 0.0, 0.0); if (_UseSceneLight > 0.0) { lightDir = ObjSpaceLightDir(v.vertex); } else { lightDir = -mul(_World2Object, _LightDir); } o.viewDir = mul(TBN, ObjSpaceViewDir(v.vertex)); o.lightDir = mul(TBN, lightDir); return o; } float4 frag(v2f i) : COLOR0 { float3 viewDir = normalize(i.viewDir); float3 lightDir = normalize(i.lightDir); float4 normalMap = tex2D(_BumpTex, i.uv); //float3 normalDir = normalize(normalMap * 2.0 - 1.0); float3 normalDir = normalize(UnpackNormal(normalMap)); float s = max(0, dot(lightDir, normalDir)); fixed3 h = normalize(viewDir + lightDir); float r = max(0, dot(h, normalDir)); float spec = pow(r, 48.0); float4 clr = tex2D(_MainTex, i.uv); float4 c; c.rgb = ((_Ambient + _Diffuse * s) * clr.rgb + spec * _Specular.rgb * clr.a * 1.5) * 1.3; c.a = clr.a; return c; } ENDCG } } Fallback "Diffuse" }
Have Fun!