法線貼圖,對主紋理凹凸顯示
? ? ? ? 建模原理
? ? ? ? ? ? ? ? 法線貼圖:切線空間,存儲xy切線,映射法線,法線信息存儲在切線空間中。
? ? ? ? ? ? ? ? 模型是否凹凸,是由模型頂點決定的,現在實現的法線貼圖,控制凹凸,實際上是配合
? ? ? ? ? ? ? ? 光照實現的,凹進去的部分,顏色偏暗,突出來的部分,顏色偏亮。
? ? ? ? 導入法線貼圖
????????????????
? ? ? ? ? ? ? ? 貼圖類型轉換為Normal Map,法線紋理類型。
? ? ? ? 法線貼圖計算
? ? ? ? ? ? ? ? 法線貼圖:存儲有與法線垂直的切線信息,切線信息存儲在切線空間中,使用內部
????????????????值(x切線,y切線)時,需要將法線轉換到世界空間中,再進行光照運算才能得到
????????????????正確的結果。調整凹凸深度的參數:用戶可配置,可以控制法線長短。
? ? ? ? Shader實現
? ? ? ? ? ? ? ? 加載兩張紋理:主紋理,光照法線紋理(切線空間存儲數據)
? ? ? ? ? ? ? ? 頂點著色器:
? ? ? ? ? ? ? ? ? ? ? ? 主紋理的UV偏移計算
? ? ? ? ? ? ? ? ? ? ? ? 法線紋理的UV偏移計算
? ? ? ? ? ? ? ? ? ? ? ? 計算切線空間到世界空間的轉換矩陣(可變),用于變換光照法線
? ? ? ? ? ? ? ? 片元著色器
? ? ? ? ? ? ? ? ? ? ? ? 解壓主紋理
? ? ? ? ? ? ? ? ? ? ? ? 解壓法線紋理,根據切線信息,轉換光照法線信息,將光照法線從切線空間,
????????????????????????轉到世界空間
????????????????????????拿法線紋理算出的光照法線,做光照運算。
相關實現代碼示例如下所示:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "CreateTest/PhongNormalTexture"
{Properties{//用于顯示材質紋理_MainTex("主紋理",2D)="white"{}//用于和主紋理混色_Color("混色",Color)=(1,1,1,1)//法線紋理_BumpTex("法線紋理",2D)="bump"{}//法線深度系數,可以控制法線高度_BumpScale("法線深度系數",Float) = 1_SpecularColor("高光反射材質顏色",Color) = (1,1,1,1)_Gloss("光暈系數",Range(8,256)) = 10}SubShader{Tags{"LightMode" = "ForwardBase"}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"//導入主紋理數據sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;//導入法線信息sampler2D _BumpTex;float4 _BumpTex_ST;float _BumpScale;//導入高光信息fixed4 _SpecularColor;float _Gloss;//從CPU過來的數據struct c2v{float4 vertex:POSITION;//從CPU傳遞過來的模型空間下,需要渲染的點float4 texcoord : TEXCOORD0; //因為兩張貼圖的像素,除顏色外完全重疊,所以紋理坐標點可以通用float4 tangent:TANGENT; //光照法線紋理,因為要計算切線空間下的信息,所以需要渲染點的切線信息float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION; //模型空間到裁剪空間轉換后的點float4 uv:TEXCOORD1; //因為要算出兩張紋理的uv坐標,所以做一個float4,xy存儲主紋理UV,zw存儲法線紋理UVfloat4 MatrixRowOne:TEXCOORD2; //用于傳遞從頂點著色器計算好的轉換矩陣float4 MatrixRowTwo:TEXCOORD3;float4 MatrixRowThree:TEXCOORD4;};v2f vert(c2v data){v2f r;r.pos = UnityObjectToClipPos(data.vertex);//兩張紋理的縮放和偏移可能不同,所以分別計算uv偏移信息,存儲在v2f.uvr.uv.xy = data.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;r.uv.zw = data.texcoord.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;//CPU傳遞過來的切線存儲在模型空間下//法線紋理中的,光照法線推算信息,是存儲在切線空間下的//CPU傳遞過來的切線信息與法線紋理中的切線信息,有轉換關系,所以可以推算出一個轉換矩陣用于切換法線//計算出來的轉換矩陣應該是從(切線空間到模型空間)的轉換//但是我們需要的是計算(從切線空間到世界空間)的轉換矩陣(因為最終的光照運算,需要在世界空間中完成)//所以應該先把CPU傳遞過來的切線信息,轉換到世界空間下//再計算切線的轉換矩陣,這時就能得到從(切線空間,到世界空間)的轉換矩陣//擁有了轉換矩陣,就可以將法線紋理中,求解的法線信息,從切線空間,轉換到世界空間下,進而可以計算光照//世界坐標系下的點的位置(片元著色器計算光照需求)float4 worldPos = mul(unity_ObjectToWorld, data.vertex);//世界空間下渲染點的法線信息float3 worldNormal = mul((float3x3)unity_ObjectToWorld, data.normal);//世界空間下切線的方向向量float3 worldTangent = mul((float3x3)unity_ObjectToWorld, data.tangent.xyz);//世界空間下計算與切線和法線垂直的線的方向向量(用于計算轉換矩陣)float3 worldBinormal=cross(worldNormal, worldTangent)* data.tangent.w;//需要將轉換矩陣,傳遞給片元著色器,用于轉換切線空間下的法線到世界空間中r.MatrixRowOne = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);r.MatrixRowTwo = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);r.MatrixRowThree = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);return r;}fixed4 frag(v2f data) :SV_Target{//世界空間下的點float3 worldPos = float3(data.MatrixRowOne.w,data.MatrixRowTwo.w,data.MatrixRowThree.w);//計算法線紋理中法線信息(重點),解出的法線在切線空間//解法線前,需要先對法線紋理貼圖進行采樣fixed3 bump = UnpackNormal(tex2D(_BumpTex, data.uv.zw));//通過縮放值,影響凹凸感bump.xy *= _BumpScale;//計算法線高度(數學公式)//法線還沒有轉換空間,所以計算出的法線,還在切線空間下bump.z = sqrt(1 - max(0, dot(bump.xy, bump.xy)));//通過頂點著色器傳遞過來的轉換矩陣,轉換法線,從切線空間到世界空間bump = float3(dot(data.MatrixRowOne.xyz, bump), dot(data.MatrixRowTwo.xyz, bump), dot(data.MatrixRowThree.xyz, bump));//解主紋理fixed4 texColor = tex2D(_MainTex, data.uv.xy) * _Color;//計算漫發射光照fixed3 diffuse = _LightColor0.rgb * texColor.rgb * max(0, dot(normalize(bump), normalize(_WorldSpaceLightPos0.xyz)));//計算高光反射光照fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos);fixed3 refDir = normalize(reflect(-_WorldSpaceLightPos0.xyz, normalize(bump)));fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, refDir)), _Gloss);//Phong光照運算fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz * texColor.rgb + diffuse + specular;return fixed4(color, 1);}ENDCG}}Fallback "Diffuse"
}
其實現效果如下圖:
左側為Standard的shader效果,右側為上面代碼下的shader效果,右側較左邊多了高光反射的相應數據,考慮到磚墻等一些粗糙表面現實情況下不會有如此明顯的高光,應用此shader文件的同學可以將Phong光照模型的公式中的高光反射部分去除,在其他需要高光反射的情況下再進行視情況添加即可,法線貼圖下第一個屬性值為法線深度系數(數值0.6處,由于Shader文件未設置成UTF-8導致的中文亂碼,讀者可自行設置Shader文件的格式或采用英文命名),可通過調節系數對應加強法線紋理效果。
以下內容作者提供一個網址,方便讀者下載Amplify Shader Editor,可找到該插件的網址如下:
amplify_shader_pack unity3D_游戲3d模型 免費下載 - 愛給網
插件中提供了很多較高質量動態Shader,有需要的可自行下載。
例:
導入Unity包后即可使用該可視化Shader編輯器,使用的Unity包例:
使用編輯器的過程:
若兩邊窗口未展開,點擊左右上角的銀灰色方框即可展開,部分使用示例如下:
更改Shader名稱:
添加并編輯某一屬性:(以Texture Sample [T]為例)
實現效果如圖:
左側黃色按鈕,黃色為未保存,單擊使其變為綠色,則成功保存期間的設置。
剩余屬性可自行探索。
實現上面代碼的功能對應在可視化編輯器中的操作結果如下所示:
其在檢查器窗口的情況如下:
該系列專欄為網課課程筆記,僅用于學習參考。??????