1. 程式人生 > >ShaderLab學習小結(十五)法線貼圖的簡單Shader

ShaderLab學習小結(十五)法線貼圖的簡單Shader

otl mvp truct 沒有 模型 視覺 有一個 rdb 值範圍

目標:賦予材質法線貼圖,並能響應光照的變化,體現出凹凸感。
場景中只有一個主平行光
找了一張法線貼圖(網上蕩的)
技術分享圖片
在unity裏別忘了把這張圖設為normalmap
技術分享圖片
先看一下,如果只是作為普通貼圖,賦在Diffuse材質上是啥效果
技術分享圖片
技術分享圖片
轉動平行光,看看有啥變化
技術分享圖片
如上圖,只是普通的貼圖,隨著平行光的轉動全體變暗變亮,沒有凹凸可言,平面就是平面
那就要編個shader來實現這張法線貼圖的價值了

Shader "Custom/TestBumpShader" {
    Properties {
        _NormalMap("Bump", 2D)=""{}      //1.
    }
    SubShader {
        pass{
            Tags{"LightMode"="ForwardBase"}     //2.
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            #include "lighting.cginc"
            sampler2D _NormalMap;
            struct v2f{                                        //3.
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
                float3 lightdir:TEXCOORD1;
                float3 wpos:TEXCOORD2;
            };
            v2f vert(appdata_tan v)
            {
                v2f o;
                o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
                o.wpos=mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv=v.texcoord.xy;

                //4.
                TANGENT_SPACE_ROTATION;
                float3 objLight=ObjSpaceLightDir(v.vertex);
                o.lightdir=mul(rotation,objLight);
                return o;
            }
            fixed4 frag(v2f IN):COLOR
            {
                float3 N =normalize(UnpackNormal(tex2D(_NormalMap,IN.uv)));   //5.
                float3 L = normalize(IN.lightdir);
                float ndotl=saturate(dot(N,L));
                fixed4 col = _LightColor0*ndotl;

                col+=UNITY_LIGHTMODEL_AMBIENT;
                return col;
            }
            ENDCG
        }
    }
}

按照代碼裏的註釋位置

1

_NormalMap("Bump", 2D)=""{}      //1.

定義一個貼圖,當然,下面的CG程序中要聲明一下

2

法線貼圖就必然涉及到光照,還是用forwardbase吧

3

struct v2f{                                        //3.
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
                float3 lightdir:TEXCOORD1;
                float3 wpos:TEXCOORD2;
            };

結構體中定義一個uv對應紋理,一個lightdir是光照方向,一個wpos是世界坐標

4

世界坐標的計算和uv值的獲取就不說了,以前有,代碼裏也有
為了使法線貼圖產生作用,要在切線空間中進行計算,這裏要把光照向量L轉到切線空間

TANGENT_SPACE_ROTATION;
float3 objLight=ObjSpaceLightDir(v.vertex);
o.lightdir=mul(rotation,objLight);

這裏的TANGENT_SPACE_ROTATION定義在unitycg.cginc中

// Declares 3x3 matrix ‘rotation‘, filled with tangent space basis
#define TANGENT_SPACE_ROTATION     float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;     float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )

所以前面忘說的一點就是vert函數的參數不用appdata_base了,而改用appdata_tan,因為TANGENT_SPACE_ROTATION中用到了v.tangent,這個在appdata_base中是沒有的。
然後用這裏的這個rotation去變換模型坐標空間中的光照方向,將其變換到切線空間中,得到我們要用的光照向量

5

float3 N =normalize(UnpackNormal(tex2D(_NormalMap,IN.uv)));   //5.

這裏對法線貼圖采樣要用unpacknormal函數,也是定義在unitycg.cginc中
看到網上有人的解釋:

//將材質貼圖對應的法線 繪制在一張貼圖上
//將貼圖對應點的單位法線向量信息float3(x,y,z) 儲存在圖對應的顏色裏color(r,g,b)裏
//其中x,y,z分別對應r,g,b
//單位法線向量 float3(x,y,z),x,y,z的取值範圍是 [-1,1]
//在法線貼圖中被壓縮在顏色的範圍[0,1]中,所以需要轉換

原因我也不太明,還在學習中,反正先當固定用法記吧
再用這個處理後的法線向量和光向量進行漫反射計算就得出最終顏色。
我們看下效果:
技術分享圖片
可以看到明暗變化顯出凹凸,而不是平面了,其實模型還是平面,這就是視覺欺騙吧。

ShaderLab學習小結(十五)法線貼圖的簡單Shader