在游戲中貼花最常見(jiàn)的地方就是用鼠標(biāo)選擇一個(gè)目標(biāo)后,地上出現(xiàn)的一個(gè)圓圈,或者范圍魔法在施放時(shí)的提示區(qū)域。
這個(gè)紋理會(huì)隨著模型和地圖的表面進(jìn)地扭曲,而非一個(gè)平面,所以,我們不論怎么做,都會(huì)進(jìn)行一個(gè)“投影”的思想,才能讓貼上去的紋理在某一個(gè)方向上看的時(shí)候,是一個(gè)完整的畫(huà)面。(我們地上的圈,就是從上往下貼的,所以你從上往下看時(shí),會(huì)看到一個(gè)完整無(wú)扭曲的圖片)。
什么? 地上是一圈?是的,但是呢,我們的紋理是方的。 我們看到是圈,并不表示我們要把紋理貼到一個(gè)圈上。
下面是我在RenderMonkey里測(cè)試的結(jié)果。
OK,圍觀完畢,下面簡(jiǎn)單說(shuō)一下如何實(shí)現(xiàn)。
用投影紋理進(jìn)行貼花分為三部。
1、正常渲染模型。
無(wú)它!
2、根據(jù)投影方向,投影半徑找到投影時(shí)需要渲染的三角形組。
這種貼花的效率損耗就是花在這里了,所以三角形剔除算法要比較高效才行。
3、將此三角形組進(jìn)行渲染,紋理映射時(shí)采用投影紋理。
渲染時(shí),要打開(kāi)全局混合開(kāi)關(guān)。
下面是投影紋理的HLSL代碼,以及相關(guān)解釋。
VS:
struct VS_INPUT
{
float4 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
float3 WorldPos : TEXCOORD1;
};
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.Position = mul( Input.Position, matViewProjection );
Output.Texcoord = Input.Texcoord;
Output.WorldPos = Input.Position;
return( Output );
}
VS所做的工作并沒(méi)有什么特別的,僅是需要多向PS傳遞一個(gè)空間位置。
PS:
sampler2D baseMap;
sampler2D Texture1; //貼這張紋理時(shí),其UV尋址方式最好為CLAMP
struct PS_INPUT
{
float2 Texcoord : TEXCOORD0;
float3 WorldPos : TEXCOORD1;
};
float4 ps_main( PS_INPUT Input ) : COLOR0
{
float3 Center = float3(0, 0, 20);//投影中心,Y值被忽略。
float Radius = 4;//投影范圍
float3 UVector = float3(1, 0, 0)/(2 * Radius);//將世界坐標(biāo)變換到紋理投影空間坐標(biāo)并規(guī)范化到0-1之間(正投影)
float3 VVector = float3(0, 0, 1)/(2 * Radius);//同上
float2 coord;
coord.x = dot(Input.WorldPos - Center, UVector) + 0.5;
coord.y = dot(Input.WorldPos - Center, VVector) + 0.5;
// if(coord.x < 1 && coord.y < 1 && coord.x>0 && coord.y>0)
return tex2D( Texture1, coord);
//else
//return 0;
}
PS所做的工作就是將世界坐標(biāo)轉(zhuǎn)換到投影空間,再轉(zhuǎn)換為紋理坐標(biāo)。
需要說(shuō)明一點(diǎn)的是,為了測(cè)試方便,我僅假設(shè)此時(shí)攝相機(jī)觀察和投影方向?yàn)?Y方向。所以
dot(Input.WorldPos - Center, UVector)+0.5
上面這句話其實(shí)相當(dāng)于是mul(Input.WorldPos,matProjTexture)/2.0+0.5;
另外,對(duì)于
// if(coord.x < 1 && coord.y < 1 && coord.x>0 && coord.y>0)
這句話,我寫(xiě)在這里,是作為裁剪使用,若沒(méi)有這個(gè). 就算你設(shè)置為了CLAMP,那么當(dāng)你的紋理邊緣的ALPHA不為0時(shí),你會(huì)看到
紋理會(huì)左右延伸。
而若你未選擇CLAMP尋址方式,那你的效果就百般神奇了。 也可以將上面屏蔽的代碼解開(kāi),用于裁剪。
結(jié)尾:
一、投影紋理進(jìn)行模型貼花時(shí),主要是進(jìn)行三角面剔除,使在渲染貼花時(shí),提交最少的三角面。
二、在貼花PASS中,需要將全局混合開(kāi)啟,并設(shè)置相應(yīng)的SRCBLEND(SRC_ALPHA)和DESTBLEND(DEST_ALPHA)值。括號(hào)內(nèi)為我用的值。
當(dāng)然,如果你不想讓貼花與場(chǎng)景(模型)混合,則可以不開(kāi)啟!
三、請(qǐng)注意紋理的尋址方式以及紋理邊緣的ALPHA情況!∪艏y理邊緣ALPHA不為0,則可以手工進(jìn)行裁剪。
四、本文僅是采用了固定的投影方向和SHADER內(nèi)部定義變量的方式來(lái)進(jìn)行貼花渲染!〔⑶,并未進(jìn)行模型三角面剔除。所以若要使用,則需要注意第一個(gè)問(wèn)題。
五、本文靈感來(lái)源于此貼:http://forums.create.msdn.com/forums/p/34339/198791.aspx
六、支持郵件交流:BoYueGame#Gmail#com