ShaderLab 法线贴图(凹凸材质)

http://www.manew.com/thread-97665-1-1.html



内容说明:
    1. 法线贴图的原理
    2. 法线贴图的unity实现

1. 法线贴图的原理
    1.1 什么是法线贴图?
    每个顶点都有一条法线,三角形内部法线由插值计算得出,颜色则是直接从纹理取数据。法线贴图的基本思想就是像纹理采样一样为法线取值。
    1.2法线纹理
    下图是一张法线的纹理图:
ShaderLab 法线贴图(凹凸材质) 
  每个纹素的RGB值实际上表示的是XYZ向量:颜色的分量取值范围为0到1,而向量的分量取值范围是-1到1;可以建立从纹素到法线的简单映射:
normal = (2*color)-1
   法线纹理的映射方式和漫反射纹理相似。麻烦之处在于如何将法线从各三角形局部空间(切线空间tangent space,亦称图像空间image space)变换到模型空间(着色计算所采用的空间)。
    1.3切线空间
    大家对矩阵已经十分熟悉了,应该知道定义一个空间需要三个向量。现在Up向量已经有了,即法线:
ShaderLab 法线贴图(凹凸材质) 
   然后是切线T:垂直于法线的向量。但这样的切线有很多个:
ShaderLab 法线贴图(凹凸材质) 
这么多切线中该选哪个呢?理论上哪一个都行。但我们必须保持连续一致性,以免衔接处出现瑕疵。Unity已经给我提供好了Tangent。
    定义一组基需要三个向量,因此我们还得计算副切线B。可以使用法线和切线叉乘计算得到。那么切空间就可以表示成如下图这样了。
ShaderLab 法线贴图(凹凸材质) 
已知T、B、N向量之后,即可得下面这个漂亮的矩阵,完成从切线空间到模型空间的变换:
ShaderLab 法线贴图(凹凸材质) 
只需对上述矩阵求逆即可得逆变换。这个矩阵(正交阵,即各向量相互正交的矩阵,参见下文“延伸阅读”小节)的逆矩阵恰好也就是其转置矩阵,计算十分简单:
ShaderLab 法线贴图(凹凸材质) 
2. unity中的实现
   我们看下unity实现的代码:
[C#] 纯文本查看 复制代码
?
 
01
02
03
04
05
06
07
08
09
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
Shader "Custom/NewSurfaceShader" {
    Properties {
                _MainText("MainText",2D)="white"{}               
                _BumpMap("BumpMap",2D)="white"{}
        }
        SubShader {
                pass{
                Tags{"LightMode"="ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
 
                float4 _LightColor0;
                sampler2D _BumpMap;
                sampler2D _MainText;
                struct v2f {
                        float4 pos:SV_POSITION;
                        float2 uv:TEXCOORD0;
                        float3 lightDir:TEXCOORD1;
                };
 
                v2f vert (appdata_full v) {
                        v2f o;
                        o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
                        o.uv=v.texcoord.xy;
                        TANGENT_SPACE_ROTATION;
                        o.lightDir= mul(_World2Object, _WorldSpaceLightPos0).xyz;//Direction Light
                        o.lightDir=mul(rotation,o.lightDir);
                        return o;
                }
                float4 frag(v2f i):COLOR
                {
                        float4 c=1;
                        float3 N=UnpackNormal(tex2D(_BumpMap,i.uv));
                        float diff=max(0,dot(N,i.lightDir));
                        c=_LightColor0*diff;
                        c*=tex2D(_MainText,i.uv);
                        return c;
                }
                ENDCG
                }
        }
}

   我们把光照贴图给附上,材质分别给一个平面和一个球看下效果:
ShaderLab 法线贴图(凹凸材质) 
ShaderLab 法线贴图(凹凸材质) 
2.3 UnpackNormal函数说明
[C#] 纯文本查看 复制代码
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
        fixed3 normal;
        normal.xy = packednormal.wy * 2 - 1;
        normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
        return normal;
}
 
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
        return packednormal.xyz * 2 - 1;
#else
        return UnpackNormalDXT5nm(packednormal);
#endif
}

可以看见不同的压缩格式解析有一定的区别,上面讲解的不是很全面,想了解更多可以自行百度下。
2.4 用Surface Shader来实现下
[C#] 纯文本查看 复制代码
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Shader "Custom/SurfaceShader" {
        Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
                _BumpMap ("Normalmap", 2D) = "bump" {}
        }
 
        SubShader {
                Tags { "RenderType"="Opaque" }
                LOD 250
 
                CGPROGRAM
                #pragma surface surf Lambert noforwardadd
                sampler2D _MainTex;
                sampler2D _BumpMap;
                struct Input {
                        float2 uv_MainTex;
                };
 
                void surf (Input IN, inout SurfaceOutput o) {
                        fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
                        o.Albedo = c.rgb;
                        o.Alpha = c.a;
                        o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
                }
                ENDCG 
        }
        FallBack Off
}

代码下载地址http://pan.baidu.com/s/1boCvoIz