默认情况下,一个模型走的是顶点法线,一个游戏模型的顶点一般很少,所以法线细节也偏低。
使用法线贴图后,在片元阶段就能应用逐像素的法线,这样对于物体表面细节的还原就更加真实。

上图显示的是物体顶点法线,其中蓝色的是法线,绿色的是切线(Tangent),红色的是副法线(或者叫副切线)。这三根线构成了当前顶点的切线空间,我们在切线空间中描述模型的表面信息,并传递到法线贴图中。一般情况下,我们可以认为法线指向z轴,切线指向x轴,父法线指向y轴
在PC端上,法线贴图一般会用DXT/BC进行压缩,只保留红通道,所以需要在Shader中使用UnpackNormal
方法,这个方法不仅会还原法线贴图,同时还会将偏移值从[0,1]转到[-1,1],因为法线本来记录的就是每个像素的法线偏移量,取值是-1到1的。
一个物体的切线是引擎自动给出的,在Unity中,切线的方向跟物体uv的u方向一致。而物体的副法线是法线和切线通过叉乘计算出来的。
想要应用法线贴图,我们需要先在顶点着色器中将顶点在世界空间下的法线、切线、副切线信息传递到片元着色器中。
下面对于法线的应用,在ForwardBase和ForwardAdd两个Pass都要实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| Properties { _NormalMap("NormalMap", 2D) = "bump" {} _NormalIntensity("NormalIntensity", Range(0.0, 5.0)) = 1.0 }
struct appdata { float4 tangent : TANGENT; };
struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 normal_dir : TEXCOORD1; float3 tangent_dir : TEXCOORD3; float3 binormal_dir : TEXCOORD4; float3 pos_world : TEXCOORD2; }; sampler2D _NormalMap; float _NormalIntensity;
v2f vert (appdata v) { v2f o; o.normal_dir = normalize(mul(float4(v.normal,0.0), unity_WorldToObject).xyz); o.tangent_dir = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); o.binormal_dir = normalize(cross(o.normal_dir, o.tangent_dir)) * v.tangent.w; return o; } half4 frag (v2f i) : SV_Target { half4 base_color = tex2D(_MainTex, i.uv); half4 ao_color = tex2D(_AOMap, i.uv); half4 spec_mask = tex2D(_SpecMask, i.uv); half4 normal_map = tex2D(_NormalMap, i.uv); half3 normal_data = UnpackNormal(normal_map); normal_data.xy = normal_data.xy * _NormalIntensity;
half3 normal_dir = normalize(i.normal_dir); half3 tangent_dir = normalize(i.tangent_dir); half3 binormal_dir = normalize(i.binormal_dir); float3x3 TBN = float3x3(tangent_dir, binormal_dir, normal_dir); normal_dir = normalize(mul(normal_data, TBN)); }
|