【風宇沖】Unity3D教程寶典之Shader篇:第二十講法線貼圖(2012-12-26 10:42:55)
轉載▼
| 標籤:unity3d教程unity3dunityit遊戲開發 |
分類: 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
- }