NormalMap 法线贴图

法线贴图+纹理贴图(细节明显)

纹理贴图

法线贴图

 

 

 

 

法线贴图

  存储法线的一张贴图,归一化的法线的 xyz 的值被映射成为对应的 RGB 值。归一化的法线值为[-1,1],RGB的每一个分量为无符号的8位组成,范围[0,255]。即法线的分量由[-1,1]映射成[0,255]。法线贴图一般呈蓝色,因为大多数朝向 (0,0,1)的法线被映射成为了 (0,0,255)。

  转换关系:

    法线转RGB:

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

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

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

    RGB转法线:

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

      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
}

 

posted @ 2015-10-22 00:38  那些消散不去的是寂寞  阅读(2837)  评论(0编辑  收藏  举报