Unity3D教程宝典之Shader篇:第二十讲法线贴图
轉載自風宇沖Unity3D教程學院
上一講我們講了凹凸貼圖以及生成法線貼圖。
這一講來談談怎么使用法線貼圖。
一:法線貼圖的原理
二:法線貼圖的實現
三:法線貼圖的使用
四:法線貼圖的格式
一:法線貼圖的原理
光照效果很大程度上是由垂直于物體表面的法線決定的,因為法線影響反射光的方向。均勻垂直的法線是鏡面貼圖。但是有時候我們會給一個平面使用磚墻貼圖,磚墻應該是凹凸不平的,而如果讓磚墻使用該平面的法線的話,畫面就會很假,神馬?一面墻像鏡子一樣反光=。=
而如果按真實磚墻去做模型的話,即做高精度模型,一方面制作麻煩,另一方面運行時對性能損耗大。
法線貼圖就是來解決這個問題的。法線貼圖就是把法線信息儲存在一張圖里。使用法線貼圖時,通常頂點數和三角形面數只有高精度模型的十分之一不到。
二:法線貼圖的實現
將材質貼圖對應的法線 繪制在一張貼圖上。將貼圖對應點的單位法線向量信息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]中,所以需要轉換:
顏色 = 0.5 * 法線 + 0.5;
線= 2 * (顏色- 0.5);
三:法線貼圖的使用
主要步驟
(1)對法線貼圖進行采樣,取得壓縮在顏色空間[0,1]里的法線
float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);
(2)將壓縮在[0,1]里的法線轉換至3D空間[-1,1] (因為是單位向量)
float3 expand(float3 v) {return(v -0.5) *2; }
之后使用該法線即可,方法與16講里一樣。
具體實現詳見本文末的腳本。
四:法線貼圖的格式
法線貼圖主要分為2個類別:
(1)RGB法線貼圖,即上面使用的。通常呈藍色。(后綴可以是常見的.png .jpg等)
(2)壓縮格式的法線貼圖。例如DXT5nm(后綴名為.dds)
dds是DirectDraw Surface的縮寫,實際上,它是DirectX紋理壓縮(DirectX Texture Compression,簡稱DXTC)的產物。DXTC減少了紋理內存消耗的50%甚至更多,有3種DXTC的格式可供使用,它們分別是DXT1,DXT3和DXT5。
有關DDS的延伸閱讀
壓縮法線貼圖的原理:
法線(x,y,z)是一條單位向量。故X2+ Y2+Z2=1。所以知道了x,y,z里的任意兩個,剩下的那個就可以通過計算得出。所以我們就可以使用2個通道的圖儲存x,y,z里的兩個值,將xyz里剩余的值省略,通過計算得出。
壓縮法線貼圖的好處:
壓縮后的法線貼圖,大小只有原來的1/4左右,故可以使用更大或者更多的貼圖來提升畫面品質。
Unity3d的法線貼圖:
Unity3d使用的壓縮法線貼圖是DXT5nm格式的。有A和G兩個通道。對于法線(x,y,z) A對應x,G對應y。
對壓縮法線貼圖的采樣依然是如下函數:
float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);
packedNormal.w對應A通道,即法線的x。
packedNormal.y對應G通道,即法線的y。
范圍依然是[0,1], 依然需要轉換至[-1,1]。
對DXT5nm法線貼圖進行轉換的函數如下,其中v傳入packedNormal
float3 expand(float3 v)
{
fixed3 normal;
normal.xy = v.wy * 2 - 1;
normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
return normal;
}
Unity3d的標準法線解壓函數是fixed3 UnpackNormal(fixed4 packednormal)。
打開UnityCG.cginc找到對應函數:
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(SHADER_API_GLES) && defined(SHADER_API_MOBILE)
return packednormal.xyz * 2 - 1;
#else
fixed3 normal;
normal.xy = packednormal.wy * 2 - 1;
normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
return normal;
#endif
}
該函數定義的如果是移動平臺或者OpenGL ES,那么斷定使用的是RGB法線貼圖,否則則為DXT5nm貼圖。
但實際上移動平臺也可以用壓縮格式的法線貼圖,而Windows也能使用RGB法線貼圖。故不建議使用UnpackNormal函數,建議根據法線貼圖的具體格式來使用自己寫的對應函數。
================================================================================================
================================================================================================
================================================================================================
腳本:
// Shader: 帶法線貼圖的Surface Shader
// Author: 風宇沖
Shader "Custom/3_NormalMap" {
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_NormalMap ("NormalMap", 2D) = "white" {}
}
Subshader
{
CGPROGRAM
#pragma surface surf BlinnPhong
struct Input
{
float2 uv_MainTex;
};
//法線范圍轉換:單位法線 float3(x,y,z),x,y,z的取值范圍是 [-1,1]。在法線貼圖中被壓縮在顏色的范圍[0,1]中,所以需要轉換
//(1)RGB法線貼圖
float3 expand(float3 v) { return (v - 0.5) * 2; }
//(2)DXT5nm法線貼圖
float3 expand2(float4 v)
{
fixed3 normal;
normal.xy = v.wy * 2 - 1;
normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
return normal;
}
sampler2D _MainTex;
sampler2D _NormalMap;
void surf(Input IN,inout SurfaceOutput o)
{
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
//對法線貼圖進行采樣,取得壓縮在顏色空間里的法線([0,1])
float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);
//要將顏色空間里的法線[0,1],轉換至真正3D空間里的法線范圍[-1,1]
//注意:范圍基本都是從[0,1]轉換至[-1,1].主要是圖的通道與法線xyz的對應關系要根據法線貼圖格式而定
//UnpackNormal, UnityCG.cginc里的函數
//o.Normal = UnpackNormal(packedNormal);
//expand,標準法線解壓函數
o.Normal = expand(packedNormal.xyz);
}
ENDCG
}
}
總結
以上是生活随笔為你收集整理的Unity3D教程宝典之Shader篇:第二十讲法线贴图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python编程8g的内存够么_详解解决
- 下一篇: jenkins docker 自动部署