目錄學習
@code
儘管在一開始,咱們在渲染中使用紋理是爲了定義一個物體的顏色,但後來人們發現,紋理其實能夠用來存儲任何表面屬性。一種常見的用法就是使用漸變紋理來控制漫反射光照的結果。在以前計算漫反射光照結果的時候,咱們都是使用表面法線和光照方向的點積結果與材質的反射率相乘來獲得表面的漫反射光照。但有時,咱們須要更加靈活的控制光照。使用這種技術,能夠保證物體的輪廓線相比於以前使用的傳統漫反射光照更加明顯,並且可以提供多種色調變化。
在本節中,咱們將學習如何用一張漸變紋理來控制漫反射光照。而後獲得相似下圖的效果。
能夠看出,使用這種方式能夠自由的控制物體的漫反射光照。不一樣的漸變紋理有不一樣的特性。例如在左邊的圖中,咱們使用一張從紫色調到淺黃的漸變紋理;而中間的漸變紋理是從黑色向淺灰色靠攏,而中間的分界線略微微發紅,這是由於畫家在插畫中每每會在陰影中使用這種色調;右邊的漸變紋理則一般被用於卡通風格的渲染,這種漸變紋理中的渲染一般是突變的,既沒有平滑過渡,以此來模擬卡通中的陰影色塊。
爲了達到上述效果,咱們須要進行如下工做:
(1)咱們在Properties語義塊中聲明一個紋理屬性來存儲漸變紋理:orm
Properties{ _Color("Color Tint",Color)={1,1,1,1} _RampTex("Ramp Tex",2D)="white"{} _Specualr("Specular",Color)={1,1,1,1} _Gloss("Gloss",Range(8.0,256))=20 }
(2)而後,咱們在Subshader語義塊中定義了一個Pass語義塊,而且在Pass的第一行指明瞭該Pass的光照模式:blog
Subshader{ Pass{ Tags{"LightMode"="ForwardBase"} } }
LightMode是Pass標籤中的一種,它用於定義該Pass在Unity的光照流水線中的角色
(3)而後,咱們使用CGPROGRAM和ENDCG開包圍住Cg代碼片,以定義最重要的頂點着色器和片元着色器代碼。咱們使用#pragma指令來告訴Unity,咱們定義的頂點着色器和片元着色器叫什麼名字。在本例中它們名字分別是vert和frag圖片
CGPROGRAM #pragma vertex vert #pragma fragment frag
(4)爲了使用Unity內置的一些變量,如_LightColor0,還須要包含進Unity的內置文件Lighting.cginc:get
#include"Lighting.cginc"
(5)隨後,咱們須要定義和Properties中各個屬性相匹配的變量:it
fixed4 _Color; sampler2D _RampTex; float4 _RampTex_ST; fixed4 _Specular; float _Gloss;
咱們爲漸變紋理_RampTex定義了它的紋理屬性變量_RampTex_ST
(6)定義頂點着色器輸入和輸出結構體class
struct a2v{ float4 vertex :POSITION; float3 normal:NORMAL: float4 texcoord:TEXCOORD0; }; struct v2f{ float4 pos:SV_POSITION; float3 worldNormal:TEXCOORD0; float3 worldPos:TEXCOORD1; float2 uv:TEXCOORD2; }
(7)定義頂點着色器:變量
v2f vert(a2v v){ v2f o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.worldNormal=UnityObjectWorldNormal(v.normal); o.worldPos=mul(_Object2World,v.vertex).xyz; o.uv=TRANSFORM_TEX(v.texcoord,_RampTex); }
咱們使用了內置的TRANSFORM_TEX宏來計算通過平鋪和偏移後的紋理座標。
(8)接下來是關鍵的片元着色器:渲染
fixed4 frag(v2f i):SV_Target{ fixed3 worldNormal=normalize(i.worldNormal); fixed3 worldLightDir=normalize(UnityWorldLightDir(i.worldPos)); fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz; //Use the texture to sample the diffuse color fixed halfLambert=0.5*dot(worldNormal,worldLightDir)+0.5; fixed3 diffuseColor=tex2D(_RampTex,fixed2(halfLambert,halfLambert)).rgb*_Color.rgb; fixed3 diffuse = _LightColor0.rgb*diffuseColor; fixed3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir=normalize(worldLightDir+viewDir); fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss); return fixed4(ambient+diffuse+specular,1.0); }
在上面的代碼中,咱們使用了之前所講的半蘭伯特模型,經過對該法線方向和光照方向的點積作一次0.5倍的縮放以及0.5倍的平移來計算半蘭伯特部分的halfLambert。這樣咱們獲得的halfLambert的範圍被映射到了[0,1]之間。以後咱們使用halfLambert來構建一個紋理座標,並用這個紋理座標對漸變紋理_RampTex進行採樣,因爲_RampTex實際上就是一個一維紋理(它在縱軸方向上顏色不變),所以紋理座標的u和v方向咱們都使用了halfLambert。而後把從漸變紋理採樣獲得的顏色和材質顏色_Color相乘,獲得最終的漫反射顏色。剩下的代碼就是計算高光反射和環境光,並把它們的結果進行相加。
(9)最後,咱們爲該UnityShader設置合適的Fallback:
Fallback"Specular"
須要注意的是。咱們須要把漸變紋理的Wrap Mode設置爲Clamp模式,以防止因爲浮點數精度而形成的問題。下圖給出了WrapMode分別爲Repeat和Clamp模式的效果對比。
能夠看出,左圖(使用Repeat模式)在高光區域有一些黑點。這是因爲浮點數精度形成的,當咱們使用fixed2(halfLambert,halfLambert)對漸變紋理進行採樣時,雖然理論上halfLambert的值在[0,1]之間,但可能會有1.00001這樣的值出現。若是咱們使用的是Repeat模式,此時就會捨棄整數部分,只保留小數部分,獲得的值就是0.00001,對應了漸變圖中最左邊的值,即黑色。所以,就會出現圖中這樣在高光區域反而有黑點的狀況。咱們只需把紋理的WrapMode設爲Clamp模式就能夠解決這種問題。