【Unity Shaders】學習筆記——SurfaceShader(七)法線貼圖


【Unity Shaders】學習筆記——SurfaceShader(七)法線貼圖


轉載請注明出處:http://www.cnblogs.com/-867259206/p/5627565.html

寫作本系列文章時使用的是Unity5.3。
寫代碼之前:

  1. 當然啦,如果Unity都沒安裝的話肯定不會來學Unity Shaders吧?

  2. 閱讀本系列文章之前你需要有一些編程的概念。

  3. 在VS里面,Unity Shaders是沒有語法高亮顯示和智能提示的,VS黨可以參考一下這篇文章使代碼高亮顯示,也可以下載shaderlabvsNShader之類的插件使代碼高亮顯示。

  4. 這是針對小白的Unity Shaders的基礎知識,如果你已經有了基礎或者你是大神,那么這些文章不適合你。

  5. 由於作者水平的局限,文中或許會有謬誤之處,懇請指出。


法線貼圖是一種在低模上模擬高模的效果的技術。這是維基對它的介紹
法線貼圖類似凹凸貼圖的升級版,凹凸貼圖記錄了物體表面凹凸的情況,法線貼圖記錄了物體表面凹凸的光照信息。光照信息即是入射光與法線的夾角信息。
為了提高性能,模型的面數越少越好,很多細節的東西都是用貼圖去彌補。但是光照是基於頂點去計算的,這樣高光陰影等光照的表現就不夠真實。於是前輩們發明了法線貼圖這個辦法,用貼圖記錄表面的光照信息,也就是用RGB值存儲法線坐標的XYZ值,使低模也能夠有高模的光照信息,從而表現出高模的光照效果。這是一種存儲空間換計算時間的方法。這篇文章介紹了3DMax制作法線貼圖的方法,看完應該會更理解法線貼圖的作用。
下面這張圖便說明了法線貼圖的原理:
img
接下來就來學習如何編寫有法線貼圖的Shader吧。


首先准備一張NormalMap,要將它的Texture Type改為Normal Map:
img
先定義幾個Properties:

    Properties 
    {
        //Add these Properties
        _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
        _NormalTex ("Normal Map", 2D) = "bump" {}
        _NormalIntensity ("Normal Map Intensity", Range(0,2)) = 1
    }

_NormalTex就是法線貼圖,_NormalIntensity是法線的強度,也就是控制凹凸的強度。
在SubShader里定義同名的變量:

        //Link the property to the CG program
        sampler2D _NormalTex;
        float4 _MainTint;
        float _NormalIntensity;

在Input結構里定義法線貼圖的紋理坐標:

        //Make sure you get the uvs for the texture in the Struct
        struct Input 
        {
            float2 uv_NormalTex;
        };

編寫surf函數:

void surf (Input IN, inout SurfaceOutput o) 
{
    //Get the normal Data out of the normal map textures
    //using the UnpackNormal() function.
    float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
    normalMap = float3(normalMap.x * _NormalIntensity,
    normalMap.y * _NormalIntensity, normalMap.z);               
    //Apply the new normals to the lighting model
    o.Normal = normalMap.rgb;
    o.Albedo = _MainTint.rgb;
    o.Alpha = _MainTint.a;
}

解釋

法線貼圖中記錄了高模的法線信息。法線的值是[-1,1],RGB的值是[0,1],所以將法線信息存儲在RGB中需要轉換。方法是((x,y,z)+(1,1,1))*0.5。也就是原先(-1,0,1)坐標變成了(0,0.5,1)。UnpackNormal函數的作用就是轉換坐標點。
在Unity安裝目錄(/Editor/Data/CGIncludes/)下的Unity.cginc文件里有UnpackNormal函數的定義。

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
}

我們看到,有兩種轉換法線的函數,這兩種函數是針對不同法線貼圖格式的。
如果是未壓縮的格式,就將法線貼圖里存儲的xyz值乘2減1,變回原來的坐標。
如果是DXT5nm壓縮格式(可以推測這應該是Unity使用的壓縮格式),則要用另外一種轉換方法。(這也是為什么要將紋理類型設置為Normal Map的原因,因為不同類型的圖片要用不同的方法轉換)
DXT5nm壓縮格式,只有G和A通道。因為法線是單位向量,所以只要知道任意兩個坐標值,就能求出另一個坐標值(z2=1-x2-y2),只要兩個通道存儲兩個坐標值即可。因為這樣的計算並不復雜,所以可以節省空間以使用更多的貼圖。DXT5nm將R通道的值移到了Alpha通道,保留G通道的值,R和B通道則以某種顏色填充。
所以轉換DXT5nm的法線貼圖,要先將A通道和G通道的值(wy)轉換為xy,再計算z值。
后面的代碼就好解釋了。轉換了法線坐標以后將法線賦值給輸出結構體的Normal變量。因為法線(0,0,1)就表明這點的法線和高模的法線相同,所以Z坐標沒有必要乘_NormalIntensity。

法線貼圖原理

法線貼圖的基本原理前面已經講過了,就是將高模的法線信息xyz存儲在rgb中,應用法線貼圖的時候將rgb值轉換為xyz,在計算光照的時候,使用高模的法線進行計算,從而得到高模的光影效果,造成低模呈現高模較為平滑的表面的假象,這是一種視覺欺騙。
這個小節里要再詳細談談法線的坐標。
要表示一個向量在三維空間的位置需要一個三維坐標系作參考。要表示法線的方位可以用世界坐標(World Space),也可以用模型坐標(Object Space)。這兩個坐標系大家應該聽說過。
如果用世界坐標,那么模型就不能旋轉和移動了。因為模型的位置改變了,它在世界坐標系里的坐標也改變了,但法線的坐標是基於世界坐標系的,這樣法線的位置就不對了。不然讀取了法線信息還要再進行世界坐標的轉換。
如果用模型坐標,就沒有世界坐標不能旋轉和移動的局限了。不管模型怎么旋轉移動,法線的位置是相對於模型的,所以法線的坐標不會變。相比於世界坐標,使用模型坐標還要進行模型坐標到世界坐標的轉換,效率低點,但靈活性更好了。
但是使用模型坐標還有局限,就是模型不能變形。因為模型變形了,法線相對模型的坐標也變了,一些會有形變的物體(比如有骨骼蒙皮動畫的模型)就不適用基於模型坐標的法線貼圖。
所以還需要另外一種坐標系,使法線貼圖不僅僅適用於某個模型,還可以用在其他模型上。
這種坐標系就是切線坐標(Tangent Space)。切線坐標就以表面上某一點的切線作為XY軸,法線作為Z軸(即垂至表面的軸)。這樣的話,符合要求的坐標系有無數種。我們可以直接將紋理的UV坐標當作XY軸,這樣就有一個現成的坐標系可以使用。
可以這樣理解切線坐標,它表示的是高模上的法線相對對應點上的低模的法線的擾動程度。當法線坐標是(0,0,1)的時候,說明高模法線和低模法線一致,相當於切線坐標表示的是高模法線相對低模法線在XY方向上的偏移程度。這也解釋了上文代碼中為什么Z軸不用乘_NormalIntensity。
法線貼圖是用於表現表面微小的凹凸的,比如皮膚皺紋、魚鱗等,所以法線的擾動值不會太大,也就是Z坐標的值是比較大,所以B通道的值比較大,所以法線貼圖通常都是藍盈盈的。
使用切線坐標也就是說每個面都有一個自己的坐標系。在Vertex Shader里進行光照計算的時候要將光線向量的坐標從World Space轉換到Tangent Space。所以我們可以在Surface Shader里使用lightDir變量和normal變量進行計算,因為Unity幫我們做了很多工作。
相比模型坐標,切線坐標更加靈活,可以應用在與原模型形狀不同的模型上,比如可以把花崗岩的法線貼圖應用在一個圓柱體表面,使圓柱體表面也具有花崗岩的凹凸效果。
法線貼圖通常有兩種,一種是Object Space Normal Map,一種是Tangent Space Normal Map。如果模型沒有動畫,那么可以使用Object Space Normal Map,如果模型有動畫,或者會變形(比如流動的水、火焰)那就要使用Tangent Space Normal Map。因為Tangent Space Normal Map要靈活的多,可以應用於不同的物體,所以Tangent Space Normal Map使用得更多一些。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM