法線紋理的使用

  1. 法線紋理一般是為了讓模型有視覺上的凹凸效果,但是模型實(shí)際不是具有凹凸面的,只是在計(jì)算光照時(shí),讓模型不同位置根據(jù)法線值的不同,得到凹凸感的光照效果。法線紋理中保存的可能是模型空間中的法線值,這種法線紋理是五顏六色的,因?yàn)樗姆ň€值在(-1,1)之間,經(jīng)過映射后得到(0,1)之間的值,顏色是多種的。而另一種法線紋理保存的是模型中每個(gè)頂點(diǎn)的切線空間中法線擾動(dòng)方向,又因?yàn)槟P椭写蟛糠猪旤c(diǎn)法線不需要擾動(dòng),所以轉(zhuǎn)換到切線空間中法線方向仍為(0,0,1),變換后為(0.5,0.5,1),因此這種法線紋理大都為偏藍(lán)色。
  2. 在計(jì)算光照時(shí)有兩種選擇,一種是在切線空間中計(jì)算,即在vertex shader中將光照計(jì)算需要的光照方向、視線方向、模型頂點(diǎn)世界坐標(biāo)以及模型頂點(diǎn)中保存的法線方向變換到切線空間中,然后在fragment shader中對(duì)法線紋理BumpTex進(jìn)行采樣,得到法線紋理中法線normal,再根據(jù)經(jīng)典光照模型計(jì)算出ambient,diffuse和specular。
  3. 在unity shader中使用法線紋理有一些要注意的地方,如法線紋理導(dǎo)入到unity中如果被設(shè)置為normalMap格式時(shí),應(yīng)該使用的解壓函數(shù),計(jì)算binormal時(shí)要確定叉乘后的方向等,以及求float3x3的模型->切線空間變換矩陣等。在實(shí)例中給予注釋說明。
  4. 使用基于切線空間的法線紋理的計(jì)算復(fù)雜點(diǎn)在于光照計(jì)算上,因?yàn)槲覀冃枰亚芯€空間的法線變換到世界空間(或者翻過來把世界空間中的光線視點(diǎn)等變換到切線空間),而求這些變換矩陣時(shí)就要仔細(xì)小心(也算對(duì)矩陣變換計(jì)算的一個(gè)自我檢驗(yàn))。
  5. 在tangentSpace中計(jì)算光照實(shí)例
Shader "FFD/BumpMap/NormalMapTangentSpace"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BumpTex ("BumpMap",2D) = "bump" {}
        _BumpScale ("BumpScale",Range(-1,1)) = 0
        _Gloss ("GLoss",range(1,30)) = 20
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
                Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "lighting.cginc"
            #include "autolight.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 vTangent : TANGENT;
                float3 vNormal : NORMAL;
            };

            struct v2f
            {
                float4 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
//              float3 tangentNormal : TEXCOORD1;
                float3 tangentView : TEXCOORD2;
                float3 tangentLight : TEXCOORD3;
            };

            sampler2D _MainTex;
            sampler2D _BumpTex;
            float4 _MainTex_ST;
            float4 _BumpTex_ST;
            float _BumpScale;
            float _Gloss;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.uv,_BumpTex);
                float3x3 objToTangent;
                float3 bitNormal = normalize(cross(v.vNormal,v.vTangent.xyz)*v.vTangent.w);
                float3 vNormal = normalize(v.vNormal);
                float3 vTangent = normalize(v.vTangent);

                //計(jì)算object->tangent轉(zhuǎn)換矩陣,構(gòu)建一個(gè)3x3的矩陣,因?yàn)樽儞Q的是向量,沒必要用4x4的矩陣。
                //這個(gè)矩陣使用的是三個(gè)向量分布為矩陣的每一行(考慮 world->camera )
                objToTangent = float3x3(vTangent.xyz,bitNormal,vNormal);

                //計(jì)算object空間光線方向或者用內(nèi)置函數(shù)計(jì)算
                float3 objLight = mul((float3x3)unity_WorldToObject,_WorldSpaceLightPos0.xyz);
//              float3 objLight = ObjSpaceLightDir(v.vertex);

                //計(jì)算object空間view方向
                float3 objView = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos.xyz,1))-v.vertex.xyz;
                o.tangentLight = mul(objToTangent,objLight);
                o.tangentView = mul(objToTangent,objView);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                float3 tangentLight = normalize(i.tangentLight);
                float3 tangentView = normalize(i.tangentView);

                //如果沒有在unity中對(duì)紋理設(shè)置為normalTexture,那么就要手動(dòng)解析切線空間中的法線
                fixed4 colBump = tex2D(_BumpTex,i.uv.zw);
                float3 tangentNormal;
                //此時(shí)沒有被壓縮,所以tangentNormal.xyz = colBump*2-1;
                //tangentNormal = UnpackNormal(colBump);//如果設(shè)置了就用內(nèi)置函數(shù)。
                tangentNormal.xy = (colBump.xy*2-1.0) * _BumpScale;
                tangentNormal.z = sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));

                //計(jì)算光照
                tangentNormal = normalize(tangentNormal);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.xyz * saturate(dot(tangentLight,tangentNormal));
                float3 H = normalize(tangentView + tangentLight);
                float3 specular=_LightColor0.xyz * pow(saturate(dot(tangentNormal,H)),_Gloss);

                col = fixed4(col.xyz*(ambient+diffuse+specular),1);
                return col;
            }
            ENDCG
        }
    }
    FallBack "Specular"
}

  1. 在切線空間計(jì)算光照有一個(gè)問題就是當(dāng)物體在不統(tǒng)一縮放情況下會(huì)產(chǎn)生光照錯(cuò)誤的情況,因此unity5.x內(nèi)置的shader中都將光照計(jì)算放到世界空間中進(jìn)行了。世界空間法線紋理光照計(jì)算如下例,注意的地方是因?yàn)橐獙⑶芯€空間中的法線變換到世界空間,就要在vert shader中將變換矩陣求出來,因?yàn)関ert->frag數(shù)據(jù)傳遞最多是float4類型的向量,所以定義3個(gè)float3的向量來組成tangent->world轉(zhuǎn)換矩陣(也可以充分利用寄存器空間,在已用三個(gè)寄存器w分量中存儲(chǔ)worldPos的三個(gè)分量)。
Shader "FFD/BumpMap/NormalMapWorldSpace"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BumpTex ("BumpTex",2D) = "bump"{}
        _Gloss("Gloss",range(1,30)) = 1
        _BumpScale ("BumpScale",range(-1,1)) =0 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags{"LightMode" = "ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "lighting.cginc"
            #include "autolight.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 tangent :TANGENT;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 uv : TEXCOORD0;
                float4 worldVertexPos : TEXCOORD1;
                float3 TtoW0 : TEXCOORD2;
                float3 TtoW1 : TEXCOORD3;
                float3 TtoW2 : NORMAL;
            };

            sampler2D _MainTex;
            sampler2D _BumpTex;
            float4 _MainTex_ST;
            float4 _BumpTex_ST;
            float _Gloss;
            float _BumpScale;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.uv,_BumpTex);
                o.worldVertexPos = mul(unity_ObjectToWorld,v.vertex);
                float3 worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
                float3 worldTangent = mul(unity_ObjectToWorld,v.tangent.xyz);
                float3 worldBitNormal = cross(worldNormal,worldTangent)*v.tangent.w;
                o.TtoW0 = float3(worldTangent.x,worldBitNormal.x,worldNormal.x);
                o.TtoW1 = float3(worldTangent.y,worldBitNormal.y,worldNormal.y);
                o.TtoW2 = float3(worldTangent.z,worldBitNormal.z,worldNormal.z);
            
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv.xy);
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                float3 worldView = normalize(_WorldSpaceCameraPos.xyz-i.worldVertexPos.xyz);

                //對(duì)bump紋理采樣得到tangent空間下的normal;
                float4 colBump = tex2D(_BumpTex,i.uv.zw);
                //如果將法線圖設(shè)置為normalMap,就用這個(gè)函數(shù),如果沒設(shè)置,用此函數(shù)會(huì)得到錯(cuò)誤的結(jié)果
                float3 normal = UnpackNormal(colBump);
                normal.xy = normal.xy*_BumpScale;
                normal.z = sqrt(1.0-dot(normal.xy,normal.xy));
                //構(gòu)建Tangent->World變換矩陣,或者直接相乘
                float3x3 TtoW = float3x3(i.TtoW0,i.TtoW1,i.TtoW2);
                float3 worldNormal = normalize(mul(TtoW,normal));

                //計(jì)算光照
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.xyz * saturate(dot(worldNormal,worldLight));
                float3 H = normalize(worldView+worldLight);
                fixed3 specular = _LightColor0.xyz * pow(saturate(dot(H,worldNormal)),_Gloss);
                col = col*fixed4(ambient+diffuse+specular,1);
                return col;
            }
            ENDCG
        }
    }
    FallBack "Specular"
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容