Unity Shaders and Effects Cookbook (3-4) 使用高光貼圖
在學習完上一節之後,已經瞭解了在Unity 中如何實現一個高光 Shader ,但是會有一個問題,就是效果看起來不切實際,如下面的問題
我用一張圖片貼到了Cube上面,然後用了一個高光材質,得到了下圖的效果。
其實這個效果還算可以,但是認真看就會發現,這個結果是不符合自然現象的。
這個箱子是木頭的,然後有鐵皮 作為封條。
首先不符合常理的是為什麼這個木頭箱子會反光!
可能木頭箱子打蠟了,然後就反光,但是為什麼打蠟的木頭 和 鐵皮 看起來是一樣的,光滑度是一樣的嗎?
轉自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
這樣一種效果是很難解釋的通的。
那麼如何模擬真實的情況,也就是該反光的地方才反光,不該反光的地方不反光?
回想一下高光的原理,高光是根據反射光與 視線的角度來求出高光的強度值的。對於上面的箱子,木材 和 鐵片是在同一個平面上的,所以求出的高光強度值是相同的。
也就是說,按照上一節的做法是不能將 鐵片 和 木材的高光強度值區分開來的。
那麼我們要想一個辦法。
首先想到的是,把鐵片 和 木材分開來,木材作為單獨的一張貼圖,鐵片作為另外一張貼圖,裡面是空白的。
燈光只作用於鐵片這一張貼圖。我們計算出來的 高光強度 Specular * 鐵片貼圖的RGB。因為鐵片中間是黑色的,所以鐵片的中間這一塊的 RGB 都是 0 ,所以實際上只有外側鐵片的地方,才真正受到了光照的影響!然後再和 木材的貼圖的顏色相加。
轉自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
由此引入這一節的知識 -- 高光貼圖。
如上面所說,需要兩個貼圖,木材這一個貼圖只接受漫反射光照,而鐵片這一個高光貼圖 接收高光。
在 Shader 中定義對應的變數
Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor("Specular Color",Color)=(1,1,1,1) _SpecularTexture("Specular Texture",2D)="white" {} _SpecularPower("Specular Power",Range(0.1,100))=1 }
我們在 Suf 函式中,對兩個紋理取樣,然後儲存到 SurfaceOutput 結構體中傳入到 光照模型函式。
然後會遇到一個問題,SurfaceOutput 結構體,是Unity 定義的一個結構體,其定義存在與 Lighting.cginc 檔案中
struct SurfaceOutput {
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
half Specular;
fixed Gloss;
fixed Alpha;
};
檢視法線,裡面並沒有用於儲存高光貼圖顏色資訊的變數!
所以這次我們要自定義一個 SurfaceOutput 結構體,新增一個 SpecularColor 變數。
struct CustomSurfaceOutput
{
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
half Specular;
fixed3 SpecularColor;
fixed Gloss;
fixed Alpha;
};
然後把 surf 和 光照模型函式中的 SurfaceOutput 都修改為自定義的 CustomSurfaceOutput
void surf (Input IN, inout CustomSurfaceOutput o)
{
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten)
{
fixed4 c;
c.rgb=s.Albedo;
c.a=s.Alpha;
return c;
}
注意,這個時候我們還沒有指定光照模型函式為 CustomPhong,所以Unity 會丟擲一堆莫名其妙的錯,這是因為 surf 中傳給 Lambert 光照模型的是 CustomSurfaceOutput,而不是預設的 SurfaceOutput 了。
轉自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
指定光照模型為 CustomPhong
CGPROGRAM
#pragma surface surf CustomPhong
因為要在 Surf 函式中處理 木頭 這個 漫反射貼圖 和 鐵皮 這個高光貼圖,原來的 Input 結構體中是隻有 漫反射貼圖的 UV資訊的,所以修改 Input結構體新增高光貼圖的 UV資訊
struct Input
{
float2 uv_MainTex;
float2 uv_SpecularTexture;
};
修改 surf 函式,根據 Input 中的UV資訊,提取當前 UV座標的顏色資訊(紋素)
void surf (Input IN, inout CustomSurfaceOutput o)
{
//不接受高光的,漫反射貼圖,例如木頭
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
//接收高光的,高光貼圖,例如鐵皮
half4 specularC=tex2D(_SpecularTexture,IN.uv_SpecularTexture);
o.SpecularColor=specularC.rgb;
//用r值作為係數,如果當前UV座標是位於鐵片裡面黑色的那一塊,那麼rgb都是0,這樣裡面黑色的那一塊其實是無效的。
o.Specular = specularC.r;
}
修改光照函式
inline fixed4 LightingCustomPhong(CustomSurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten)
{
//首先計算漫反射;
float diffuse=max(0,dot(s.Normal,lightDir));
//計算漫反射顏色;
float3 diffuseColor=_LightColor0*s.Albedo * diffuse;
//計算反射光方向向量
float3 halfReflectVector=normalize(lightDir + viewDir);
//計算反射光強度;如果當前位置是鐵片黑色的那一塊,那麼Specular是0,這裡就沒有高光了。
float specular = pow( max(0,dot(s.Normal,halfReflectVector)) , _SpecularPower) * s.Specular;
//計算高光顏色 高光貼圖取樣顏色 * 反射光強度 * 編輯器中指定的高光顏色 * 光照顏色;
float3 specularColor =_LightColor0.rgb* s.SpecularColor * specular * _SpecularColor.rgb *(atten*5);
fixed4 c;
c.rgb=diffuseColor + specularColor;
c.a=s.Alpha;
return c;
}
最終完成得到結果
示例工程下載:
http://pan.baidu.com/s/1dFyiyDb