【Unity Shader】使用法線貼圖(Normal Map)的Shader

來源:互聯網
上載者:User

標籤:世界   dsp   ack   不同   包含   custom   之間   效果   兩種   

為何要用法線貼圖

為了提升模型表現細節而又不增加效能消耗,所以不選擇提高模型的面數,而是給模型的材質Shader中使用上法線貼圖(Normal Map),通過更改模型上的點的法線方向,增加光影凹凸效果,從而提升模型表現細節。使用法線貼圖能使一個三角面表現出凹凸的視覺效果!

法線貼圖原理

http://www.cnblogs.com/tekkaman/p/3992352.html

上面的文章解釋了很多問題:

  • 法線被儲存在切線空間(Tangent Space Normal)中,所以法線貼圖看上去呈藍色的,如果儲存在世界空間中,則各個方向會表現出不同的顏色值。
  • 為何不選擇將法線儲存在世界空間中(World Space Normal)或是模型空間中(Object Space Normal)。
  • 在使用法線貼圖時,可以將光向量轉換到Tangent Space裡做計算,也可以把法線向量轉換到World Space與光向量進行計算,結果是一樣的,但為何選擇使用前一種方法?因為後者每個點都要進行一次空間座標變換,而由於光向量是平行光,所以前一種方法只需計算一次。
法線貼圖的儲存與使用

法線(Normal)每個軸向的取值範圍為-1到1,而顏色值(Pixel)的取值範圍為0到1。所以在儲存(法線方向儲存為法線貼圖)和使用(在程式中將法線貼圖每個點的顏色轉變為法線方向)時,存在一個簡單的計算轉換過程。

  • 儲存法線貼圖 Pixel = ( Normal + 1 ) / 2
  • 使用法線貼圖 Normal = Pixel * 2 - 1

因為法線貼圖使用的是切線空間,所以以上轉換也是在切線空間下進行的。使用中還要注意光照方向的空間轉換問題。

 例子

現在準備好紋理貼圖和法線貼圖,編寫一個簡單的使用法線貼圖的Shader例子。

Shader "Custom/13-Rock NormalMap" {     Properties{        _MainTex("Main Tex", 2D) = "white"{} // 紋理貼圖        _Color("Color", Color) = (1,1,1,1)   // 控制紋理貼圖的顏色        _NormalMap("Normal Map", 2D) = "bump"{} // 表示當該位置沒有指定任何法線貼圖時,就使用模型頂點內建的法線        _BumpScale("Bump Scale", Float) = 1  // 法線貼圖的凹凸參數。為0表示使用模型原來的發現,為1表示使用法線貼圖中的值。大於1則凹凸程度更大。    }    SubShader{        Pass {            // 只有定義了正確的LightMode才能得到一些Unity的內建光照變數            Tags{"LightMode" = "ForwardBase"}            CGPROGRAM// 包含unity的內建的檔案,才可以使用Unity內建的一些變數#include "Lighting.cginc" // 取得第一個直射光的顏色_LightColor0 第一個直射光的位置_WorldSpaceLightPos0(即方向)#pragma vertex vert#pragma fragment frag             fixed4 _Color;            sampler2D _MainTex;            float4 _MainTex_ST; // 命名是固定的貼圖名+尾碼"_ST",4個值前兩個xy表示縮放,後兩個zw表示位移            sampler2D _NormalMap;            float4 _NormalMap_ST; // 命名是固定的貼圖名+尾碼"_ST",4個值前兩個xy表示縮放,後兩個zw表示位移            float _BumpScale;                struct a2v            {                float4 vertex : POSITION;    // 告訴Unity把模型空間下的頂點座標填充給vertex屬性                float3 normal : NORMAL;        // 不再使用模型內建的法線。保留該變數是因為切線空間是通過(模型裡的)法線和(模型裡的)切線確定的。                float4 tangent : TANGENT;    // tangent.w用來確定切線空間中座標軸的方向的。                float4 texcoord : TEXCOORD0;             };            struct v2f            {                float4 position : SV_POSITION; // 聲明用來儲存頂點在裁剪空間下的座標                //float3 worldNormal : TEXCOORD0;  // 不再使用世界空間下的法線方向                float3 lightDir : TEXCOORD0;   // 切線空間下,平行光的方向                float3 worldVertex : TEXCOORD1;                float4 uv : TEXCOORD2; // xy儲存MainTex的紋理座標,zw儲存NormalMap的紋理座標            };            // 計算頂點座標從模型座標系轉換到裁剪面座標系            v2f vert(a2v v)            {                v2f f;                f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是內建矩陣。該步驟用來把一個座標從模型空間轉換到剪裁空間                                // 法線方向。把法線方向從模型空間轉換到世界空間                //f.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject); // 反過來相乘就是從模型到世界,否則是從世界到模型                f.worldVertex = mul(v.vertex, unity_WorldToObject).xyz;                                //f.uv = v.texcoord.xy; // 不使用縮放和位移                f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; // 貼圖的紋理座標                f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw; // 法線貼圖的紋理座標                TANGENT_SPACE_ROTATION; // 調用這個宏會得到一個矩陣rotation,該矩陣用來把模型空間下的方向轉換為切線空間下                //ObjSpaceLightDir(v.vertex); // 得到模型空間下的平行光方向                f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); // 切線空間下,平行光的方向                return f;            }            // 要把所有跟法線方向有關的運算,都放到切線空間下。因為從法線貼圖中取得的法線方向是在切線空間下的。            fixed4 frag(v2f f) : SV_Target             {                // 環境光線                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;                                // 法線方向。從法線貼圖中擷取。法線貼圖的顏色值 --> 法線方向                //fixed3 normalDir = normalize(f.worldNormal);   // 不再使用模型內建的法線                fixed4 normalColor = tex2D(_NormalMap, f.uv.zw); // 在法線貼圖中的顏色值                //fixed3 tangentNormal = normalize(normalColor.xyz * 2 - 1); // 切線空間下的法線方向,發現計算得到的法線不正確!                fixed3 tangentNormal = UnpackNormal(normalColor); // 使用Unity內建的方法,從顏色值得到法線在切線空間的方向                tangentNormal.xy = tangentNormal.xy * _BumpScale; // 控制凹凸程度                tangentNormal = normalize(tangentNormal);                // 光照方向。                fixed3 lightDir = normalize(f.lightDir); // 切線空間下的光照方向                                // 紋理座標組應的紋理圖片上的點的顏色                fixed3 texColor = tex2D(_MainTex, f.uv.xy) * _Color.rgb;                                // 漫反射Diffuse顏色 = 直射光顏色 * max(0, cos(光源方向和法線方向夾角)) * 材質自身色彩(紋理對應位置的點的顏色)                fixed3 diffuse = _LightColor0 * max(0, dot(tangentNormal, lightDir)) * texColor; // 顏色融合用乘法                            // 最終顏色 = 漫反射 + 環境光線                 fixed3 tempColor = diffuse + ambient * texColor; // 讓環境光線也跟紋理顏色做融合,防止環境光線使得紋理效果看起來朦朧                return fixed4(tempColor, 1); // tempColor是float3已經包含了三個數值            }            ENDCG        }            }    FallBack "Diffuse"}

效果如,左邊是使用法線貼圖(凹凸參數為1,完全使用法線貼圖中的法線方向),右邊未使用法線貼圖:

注意點:

  • TANGENT_SPACE_ROTATION宏的使用。調用這個宏會得到一個矩陣rotation,該矩陣用來把模型空間下的方向轉換為切線空間下。
  • ObjSpaceLightDir()方法,得到模型空間下當前點到光源方向的向量,即平行光方向。
  • 使用mul(rotation, ObjSpaceLightDir(v.vertex)); 得到切線空間下平行光的方向。
  • 在從顏色值轉換為切線空間下的法線方向時,發現用Normal = Pixel * 2 - 1計算得到是法線不正確,效果很奇怪,如。改為使用Unity內建的UnpackNormal()方法來計算。

  • 定義了屬性_BumpScale來控制凹凸程度。當該屬性值為0時,使用模型內建的法線方向;當屬性值為1時,使用完全使用法線貼圖中的法線方向。當值位於0和1之間,則是兩種方向的過度,通過在編輯器監視面板中拖拽修改該屬性值,能夠直觀看到模型的變化效果。當該屬性值大於1,則凹凸程度更加強烈。是當該值設為7時的情況。

學習資料:

  • http://www.sikiedu.com/course/37/task/456/show#
  • http://www.sikiedu.com/course/37/task/458/show#

 

【Unity Shader】使用法線貼圖(Normal Map)的Shader

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.