法线贴图的每个像素包含一个向量,指向一个特定的方向。方向向量的范围在[-1,1]之间,而颜色所表示的范围是在[0,1]之间,所以颜色和方向的映射关系为:color=(v+1)*0.5。一般来说,法线贴图存储着每个点在各自的切线空间中的shading法线偏移(扰动)方向,如果一个点的法线方向不变,那么顶点在切线空间中的方向向量为(0,0,1),扰动方向也是在(0,0,1)的周围,根据刚才的映射公式得到颜色值为(0.5,0.5,1),所以显示出来的法线贴图是浅蓝紫色了。法线贴图可以用3dmax,blender等建模软件烘培。

  法线贴图解码出来的法线信息是在切线空间,不是世界空间的法线。所以,我们需要把法线从切线空间转到世界空间。先从模型获取网格数据,根据语义绑定,能得到法线方向和切线方向。

struct MeshData
{
  float4 vertex:POSITION;
  float3 normal:NORMAL; //模型空间的法线
  float3 tangent:TANGENT;  //切线 }

Interpolators vert(MeshData v){
  Interpolators o;
  o.vertex=UnityObjectToClipPos(v.vertex);
  o.normal=UnityObjectToWorldNormal(v.normal);
  o.tangent=UnityObjectToWorldDir(v.tangent.xyz);
  o.bitangent=cross(o.normal,o.tangent)*(v.tangent.w*unity_WorldTransformParams.w);
  o.wPos=mul(unity_ObjectToWorld,v.vertex);
}

 v.tangent.w :值为-1或者1,由引擎中的切线自动生成,和顶点的环绕顺序有关。

 w*unity_WorldTransformParams.w  :定义于unityShaderVariables.cginc中.模型的Scale值是三维向量,即xyz,当这三个值中有奇数个值为负时(1个或者3个值全为负时),unity_WorldTransformParams.w=-1,否则为1.

这两个主要是防止翻转的问题。

 

再看看片元阶段

float4 frag(Interpolators i):SV_Target{
  //因为要用法线贴图的法线,这里的原始网格法线就注释了
  //float3 N=normalize(i.normal);
  //
tex2D(_Normals,i.uv)得到的是颜色值,在(0,1)之间,因为UnpackNormal()解码出来的是向量值,
    即UnpackNormal()里做了映射处理,colorValue*2-1,映射在(-1,1)之间的范围,

    即是顶点的法线方向 
  float3 tangentSpaceNormal=UnpackNormal(tex2D(_Normals,i.uv));
  float3x3 mtxTangToWorld={
    i.tangent.x,i.bitangent.x,i.normal.x,
    i.tangent.y,i.bitangent.y,i.normal.y,
    i.tangent.z,i.bitangent.z,i.normal.z
  };
  
  float3 N=mul(mtxTangToWorld,tangentSpaceNormal);
  float3 L=normalize(UnityWorldSpaceLightDir(i.wPos));
  float attenuation=LIGHT_ATTENUATION(i);
  float3 lambert=saturate(dot(N,L));
  float3 diffuseLight=(lambert*attenuation)*_LightColor0.xyz;

  //specualr lighting
  float3 V=normalize(_WorldSpaceCameraPos-i.wPos);
  float3 H=normalize(L+V); //半角向量
  float3 specularLight=saturate(dot(H,N))*(lambert>0); //Bling-Phong
  float specularExponent=exp2(_Gloss*11)+2;
  specularLight=pow(specularLight,specularExponent)*_Gloss*attenuation;
  specularLight*=_LightColor0.xyz;
  return float4(diffuseLight*_Color+specularLight,1);
}

  以上代码里,构建矩阵,把法线信息从切线空间转到世界空间。利用该空间下的法线信息去计算模型光照。

 

 

posted on 2021-05-06 21:00  魔域大道  阅读(538)  评论(0)    收藏  举报