zoukankan      html  css  js  c++  java
  • 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
    }
  • 相关阅读:
    [C/C++] 指针数组和数组指针
    [计算机网络] DNS劫持和DNS污染
    [计算机网络-数据链路层] CSMA、CSMA/CA、CSMA/CD详解
    [BinaryTree] 二叉树常考知识点
    NODE-windows 下安装nodejs及其配置环境
    MATLAB/Excel-如何将Excel数据导入MATLAB中
    Excel-怎样实现行列转置
    一篇文章学懂Shell脚本
    SQL-MySQL使用教程-对MySQL的初步尝试
    资源贴-在线编译环境推荐
  • 原文地址:https://www.cnblogs.com/jqm304775992/p/4898550.html
Copyright © 2011-2022 走看看