1. 程式人生 > >Unity Shaders and Effects Cookbook (3-4) 使用高光貼圖

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