这几天一直在为了研究清楚 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!