NormalMap 法線貼圖

法線貼圖+紋理貼圖(細節明顯)

紋理貼圖html

法線貼圖ide

 

 

 

 

法線貼圖

  存儲法線的一張貼圖,歸一化的法線的 xyz 的值被映射成爲對應的 RGB 值。歸一化的法線值爲[-1,1],RGB的每個份量爲無符號的8位組成,範圍[0,255]。即法線的份量由[-1,1]映射成[0,255]。法線貼圖通常呈藍色,由於大多數朝向 (0,0,1)的法線被映射成爲了 (0,0,255)。函數

  轉換關係:spa

    法線轉RGB:code

      R = (x+1)/2*255;orm

      G = (y+1)/2*255;htm

      B = (z+1)/2*255;blog

    RGB轉法線:get

      x = (R/255*2)-1;it

      y = (G/255*2)-1;

      z = (B/255*2)-1;

切空間(Tangent Space,TBN):紋理空間

  切空間是在某一點全部的切向量組成的線性空間。也就是說,在模型每一個頂點中,都存在這樣的一個切空間座標系,以模型頂點爲中心,再加上TBN3個軸(Tangent,Binormal,Normal),N是頂點的法線方向,T、B兩個向量是頂點切平面的2個向量,通常T的方向是紋理座標u的方向,B的方向經過TN叉乘計算獲得。而法線貼圖就是記錄該空間下頂點的法線方向,它不是固定(0,0,1)而是在切空中間中的擾動值。

  首先,咱們須要計算出切空間到模型空間的變換矩陣。切空間由3個向量定義,Tangent,Binormal,Normal;咱們在模型頂點中已知Tangent和Normal的值,那麼Binormal能夠經過前2個向量的叉乘來取得。

    

1             struct vertexInput{
2                 float4 vertex:POSITION;
3                 float3 normal:NORMAL;
4                 float4 texcoord:TEXCOORD0;
5                 float4 tangent:TANGENT;
6             };

   在頂點函數中計算三個軸:

    

1                 o.normal = normalize(v.normal);
2                 o.tangent = normalize(v.tangent-v.normal*v.tangent*v.normal);
3                 o.binormal = cross(v.normal, v.tangent)*v.tangent.w;

  其中:切線向量o.tangent經過Gram-Schmidt修正,使它與法線垂直;副切線向量o.binormal經過乘以v.tangent.w計算它的長度。          

    有了這3個切向量,咱們就能夠定義變換矩陣:

1           float3x3 local2ObjectTranspose=float3x3(
2                     i.tangent,
3                     i.binormal,
4                     i.normal
5                 );

  在該矩陣中,沒有平移矩陣,只有旋轉矩陣,旋轉矩陣又是正交矩陣,TBN兩兩垂直,正交矩陣的逆矩陣就是其轉置矩陣。

  這個矩陣是模型空間到切空間的轉換矩陣。

法線Shader:

在切空間計算:

源代碼:

  1 //在切空間計算光源方向
  2 Shader "JQM/NoamalMap_1"
  3 {
  4     Properties
  5     {
  6         _MainTex ("Texture", 2D) = "white" {}                
  7         _BumpMap ("Normal Texture", 2D) = "bump" {}        
  8         _BumpDepth("_Bump Depth",Range(-2,2.0)) = 1
  9     }
 10 
 11     SubShader
 12     {
 13 
 14         Pass
 15         {
 16             Tags { "LightMode"="ForwardBase" }
 17 
 18             CGPROGRAM
 19             #pragma vertex vert
 20             #pragma fragment frag
 21             
 22             #include "UnityCG.cginc"
 23 
 24             //使用自定義變量
 25             sampler2D _MainTex;
 26             float4 _MainTex_ST;
 27             sampler2D _BumpMap;
 28             float4 _BumpMap_ST;
 29             uniform float _BumpDepth;
 30 
 31             //使用Unity定義的變量
 32             uniform float4 _LightColor0;
 33 
 34             //輸入結構體
 35             struct vertexInput{
 36                 float4 vertex:POSITION;
 37                 float3 normal:NORMAL;
 38                 float4 texcoord:TEXCOORD0;
 39                 float4 tangent:TANGENT;
 40             };
 41 
 42             //輸出結構體
 43             struct vertexOutput{
 44                 float4 pos:SV_POSITION;
 45                 float4 tex:TEXCOORD0;
 46                 float4 posWorld:TEXCOORD1;
 47                 float3 normal:TEXCOORD2;
 48                 float3 tangent:TEXCOORD3;
 49                 float3 binormal:TEXCOORD4;
 50             };
 51 
 52             vertexOutput vert (vertexInput v)
 53             {
 54                 vertexOutput o;
 55 
 56                 o.normal = normalize(v.normal);
 57                 o.tangent = normalize(v.tangent-v.normal*v.tangent*v.normal);
 58                 o.binormal = cross(v.normal, v.tangent)*v.tangent.w;
 59 
 60                 o.posWorld = mul(_Object2World, v.vertex);
 61                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
 62                 o.tex = v.texcoord;
 63 
 64                 return o;
 65             }
 66             
 67             fixed4 frag (vertexOutput i) : COLOR
 68             {
 69                 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz- i.posWorld.xyz);
 70                 float3 lightDirection; 
 71                 float atten;
 72 
 73                 if(_WorldSpaceLightPos0.w==0.0)//平行光
 74                 {
 75                     atten = 1.0;
 76                     lightDirection = normalize(_WorldSpaceLightPos0.xyz);
 77                 }
 78                 else
 79                 {
 80                     float3 fragmentToLightSource = _WorldSpaceLightPos0.xyz -i.posWorld.xyz;
 81                     float distance = length(fragmentToLightSource);
 82                     atten  = 1.0/distance;
 83                     lightDirection = normalize(fragmentToLightSource);
 84                 }
 85 
 86                 //Texture Map
 87                 float4 tex  = tex2D(_MainTex,i.tex.xy*_MainTex_ST.xy+_MainTex_ST.zw);
 88                 float4 texN  = tex2D(_BumpMap,i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw);
 89 
 90                 //UnpackNormal [0,1] 轉換成[-1,1]
 91                 float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0);
 92                 localCoords.z = _BumpDepth;
 93 
 94                 float3x3 Tangent2ObjectTranspose = float3x3(
 95                     i.tangent,
 96                     i.binormal,
 97                     i.normal
 98                 );
 99 
100                 //在切空間計算光照
101                 lightDirection = normalize(mul(_World2Object,lightDirection));
102                 lightDirection = normalize(mul(Tangent2ObjectTranspose,lightDirection));//轉置矩陣=逆矩陣:TBN兩兩垂直    
103                 
104                 float3 diffuseReflection =  saturate( dot(localCoords,lightDirection));    
105 
106                 return float4(diffuseReflection*tex.xyz,1.0);
107             }
108             ENDCG
109         }
110     }
111 }
View Code

 在世界空間計算:

  1 //在世界空間計算光源方向
  2 Shader "JQM/NoamalMap_2"
  3 {
  4     Properties
  5     {
  6         _MainTex ("Texture", 2D) = "white" {}                
  7         _BumpMap ("Normal Texture", 2D) = "bump" {}        
  8         _BumpDepth("_Bump Depth",Range(-2,2.0)) = 1
  9     }
 10 
 11     SubShader
 12     {
 13 
 14         Pass
 15         {
 16             Tags { "LightMode"="ForwardBase" }
 17 
 18             CGPROGRAM
 19             #pragma vertex vert
 20             #pragma fragment frag
 21             
 22             #include "UnityCG.cginc"
 23 
 24             //使用自定義變量
 25             sampler2D _MainTex;
 26             float4 _MainTex_ST;
 27             sampler2D _BumpMap;
 28             float4 _BumpMap_ST;
 29             uniform float _BumpDepth;
 30 
 31             //使用Unity定義的變量
 32             uniform float4 _LightColor0;
 33 
 34             //輸入結構體
 35             struct vertexInput{
 36                 float4 vertex:POSITION;
 37                 float3 normal:NORMAL;
 38                 float4 texcoord:TEXCOORD0;
 39                 float4 tangent:TANGENT;
 40             };
 41 
 42             //輸出結構體
 43             struct vertexOutput{
 44                 float4 pos:SV_POSITION;
 45                 float4 tex:TEXCOORD0;
 46                 float4 posWorld:TEXCOORD1;
 47                 float3 normalWorld:TEXCOORD2;
 48                 float3 tangentWorld:TEXCOORD3;
 49                 float3 binormalWorld:TEXCOORD4;
 50             };
 51 
 52             vertexOutput vert (vertexInput v)
 53             {
 54                 vertexOutput o;
 55 
 56                 o.normalWorld = normalize(mul(float4(v.normal,0.0),_World2Object).xyz);//法線向量轉世界座標,不一樣於頂點,他須要乘以模型轉換矩陣的逆的轉置;
 57                 o.tangentWorld = normalize(mul(_Object2World,v.tangent).xyz);
 58                 o.tangentWorld = o.tangentWorld-o.normalWorld*o.tangentWorld*o.normalWorld;//修正不垂直,使他們垂直
 59                 o.binormalWorld = cross(o.normalWorld, o.tangentWorld);
 60 
 61                 o.posWorld = mul(_Object2World, v.vertex);
 62                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
 63                 o.tex = v.texcoord;
 64 
 65                 return o;
 66             }
 67             
 68             fixed4 frag (vertexOutput i) : COLOR
 69             {
 70                 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz- i.posWorld.xyz);
 71                 float3 lightDirection; 
 72                 float atten;
 73 
 74                 if(_WorldSpaceLightPos0.w==0.0)//平行光
 75                 {
 76                     atten = 1.0;
 77                     lightDirection = normalize(_WorldSpaceLightPos0.xyz);
 78                 }
 79                 else
 80                 {
 81                     float3 fragmentToLightSource = _WorldSpaceLightPos0.xyz -i.posWorld.xyz;
 82                     float distance = length(fragmentToLightSource);
 83                     atten  = 1.0/distance;
 84                     lightDirection = normalize(fragmentToLightSource);
 85                 }
 86 
 87                 //Texture Map
 88                 float4 tex  = tex2D(_MainTex,i.tex.xy*_MainTex_ST.xy+_MainTex_ST.zw);
 89                 float4 texN  = tex2D(_BumpMap,i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw);
 90 
 91                 //UnpackNormal [0,1] 轉換成[-1,1]
 92                 float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0);
 93                 localCoords.z = _BumpDepth;
 94 
 95                 float3x3 Tangent2WorldTranspose = float3x3(
 96                     i.tangentWorld,
 97                     i.binormalWorld,
 98                     i.normalWorld
 99                 );
100 
101                 //將法線轉到世界空間
102                 localCoords = normalize(mul(localCoords,Tangent2WorldTranspose));
103 
104                 float3 diffuseReflection =  saturate( dot(localCoords,lightDirection));    
105 
106                 return float4(diffuseReflection*tex.xyz,1.0);
107             }
108             ENDCG
109         }
110     }
111 }
View Code

 

    

Unity 3D 的UnityCG.cginc文件定義的切空間旋轉矩陣:

1 #define TANGENT_SPACE_ROTATION \
2     float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; \
3     float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )

 

將攝像機(視點)轉換到模型空間:

// Computes object space view direction
inline float3 ObjSpaceViewDir( in float4 v )
{
    float3 objSpaceCameraPos = mul(_World2Object, float4(_WorldSpaceCameraPos.xyz, 1)).xyz;
    return objSpaceCameraPos - v.xyz;
}

將光源轉換到模型空間:

// Computes object space light direction
inline float3 ObjSpaceLightDir( in float4 v )
{
    float3 objSpaceLightPos = mul(_World2Object, _WorldSpaceLightPos0).xyz;
    #ifndef USING_LIGHT_MULTI_COMPILE
        return objSpaceLightPos.xyz - v.xyz * _WorldSpaceLightPos0.w;
    #else
        #ifndef USING_DIRECTIONAL_LIGHT
        return objSpaceLightPos.xyz - v.xyz;
        #else
        return objSpaceLightPos.xyz;
        #endif
    #endif
}
相關文章
相關標籤/搜索