多重紋理從原則上講全部可以被多次渲染所替代,因?yàn)槎嘀丶y理實(shí)際上就是將多次渲染中的每遍中的紋理在一遍中進(jìn)行操作。因此我們首先介紹一下多次渲染,然后概要介紹一下多重紋理的使用方法和實(shí)現(xiàn)的效果。
一種復(fù)雜的效果往往是無(wú)法通過(guò)單次渲染來(lái)完成的,因此多次渲染實(shí)際上非常普遍。在shadow volume算法中就要通過(guò)多次渲染來(lái)實(shí)現(xiàn)陰影混合。Hook在siggraph1998中提到Quake3使用了10次渲染:
5 漫反射光照(Diffuse lighting)
6 基本紋理,具有鏡面反射成分
7 鏡面光照(Specular lighting)
8 放射性光照(Emissive lighting)
9 體積/大氣效果(volumetric/atmosphere effect)
10 屏幕顯示(screen flash)
我們來(lái)看一下多次渲染和多重紋理的代碼比較。
- 多次渲染:
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
m_pd3dDevice->SetTexture(0,m_pWallTexture);
m_pd3dDevice->SetSteamSource(0,m_pCubeVB,0,sizeof(CUBEVERTEXT));
m_pd3dDevice->SetFVF(FVF_CUBEVERTEXT);
m_pd3dDevice->SetIndices(m_pCubeIB,0);
m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,24,0,36/3);
//texture #2
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
m_pd3dDevice->SetTexture(0,m_pDetailTexture);
m_pd3dDevice->SetSteamSource(0,m_pCubeVB,0,sizeof(CUBEVERTEXT));
m_pd3dDevice->SetFVF(FVF_CUBEVERTEXT);
m_pd3dDevice->SetIndices(m_pCubeIB);
m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,24,0,36/3);
- 多重紋理
m_pd3dDevice->SetTexture(0,m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
//texture #2
m_pd3dDevice->SetTexture(1,m_pDetailTexture);
m_pd3dDevice->SetTextureStageState(1,D3DTSS_TEXCOORDINDEX,0);
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_CURRENT);
m_pd3dDevice->SetSteamSource(0,m_pCubeVB,0,sizeof(CUBEVERTEXT));
m_pd3dDevice->SetFVF(FVF_CUBEVERTEXT);
m_pd3dDevice->SetIndices(m_pCubeIB);
m_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,24,0,36/3);
通過(guò)對(duì)比可以看到多重紋理只對(duì)場(chǎng)景頂點(diǎn)進(jìn)行了一次繪制,而多次渲染要進(jìn)行對(duì)此繪制。
上邊代碼也基本展示了多重紋理的使用方法:設(shè)置每一個(gè)紋理階段的紋理操作參數(shù)和操作類(lèi)型,然后調(diào)用頂點(diǎn)繪制即可。
在一個(gè)紋理階段有最多三個(gè)紋理參數(shù),通過(guò)設(shè)置的某種操作將結(jié)果投入下一個(gè)階段。
這樣進(jìn)行多重紋理級(jí)聯(lián)以后的效果如下:
具體來(lái)說(shuō),紋理的操作包括:
typedef enum _D3DTEXTUREOP {
D3DTOP_SELECTARG1 = 2,
D3DTOP_SELECTARG2 = 3,
D3DTOP_MODULATE = 4,
D3DTOP_MODULATE2X = 5,
D3DTOP_MODULATE4X = 6,
D3DTOP_ADD = 7,
D3DTOP_ADDSIGNED = 8,
D3DTOP_ADDSIGNED2X = 9,
D3DTOP_SUBTRACT = 10,
D3DTOP_ADDSMOOTH = 11,
D3DTOP_BLENDDIFFUSEALPHA = 12,
D3DTOP_BLENDTEXTUREALPHA = 13,
D3DTOP_BLENDFACTORALPHA = 14,
D3DTOP_BLENDTEXTUREALPHAPM = 15,
D3DTOP_BLENDCURRENTALPHA = 16,
D3DTOP_PREMODULATE = 17,
D3DTOP_MODULATEALPHA_ADDCOLOR = 18,
D3DTOP_MODULATECOLOR_ADDALPHA = 19,
D3DTOP_BUMPENVMAP = 22,
D3DTOP_BUMPENVMAPLUMINANCE = 23,
D3DTOP_DOTPRODUCT3 = 24,
D3DTOP_MULTIPLYADD = 25,
D3DTOP_LERP = 26,
D3DTOP_FORCE_DWORD = 0x7fffffff
這里邊也包括了alpha混合的操作,都是通過(guò)D3DTEXTUREOP來(lái)進(jìn)行設(shè)置。
對(duì)于每個(gè)顏色的操作最多有三個(gè)參數(shù),也是通過(guò) SetTextureStageState()來(lái)設(shè)置的,該方法的第一個(gè)參數(shù)設(shè)置了紋理階段,第二個(gè)參數(shù)設(shè)置了參數(shù)序號(hào): D3DTSS_COLORARG0,D3DTSS_COLORARG1,D3DTSS_COLORARG2。第三個(gè)參數(shù)是參數(shù)值:
D3DTA_DIFFUSE
D3DTA_SELECTMASK
D3DTA_SPECULAR
D3DTA_TEMP
D3DTA_TEXTURE
D3DTA_TFACTOR
分別表示了不同的顏色獲取途徑。
OK,下面我們來(lái)看一下幾種多重紋理的效果。
1,單色紋理貼圖
一些老的三維加速卡不支持使用目標(biāo)像素的阿爾法值進(jìn)行紋理混合,更多信息請(qǐng)參閱阿爾法紋理混合。一般來(lái)說(shuō)這些加速卡也不支持多重紋理混合,如果應(yīng)用程序在此類(lèi)適配器上運(yùn)行,那么可以用多趟紋理混合進(jìn)行單色光照貼圖
要進(jìn)行單色光照貼圖,應(yīng)用程序應(yīng)該把光照信息存放在光照貼圖的阿爾法數(shù)據(jù)中。應(yīng)用程序使用Microsoft? Direct3D?的紋理過(guò)濾功能把圖元的圖像中的每個(gè)像素映射到光照貼圖中的相應(yīng)texel。應(yīng)用程序應(yīng)該把源混合因子設(shè)為相應(yīng)texel的阿爾法值.
以下C++示例代碼描述了應(yīng)用程序如何把一張紋理用作單色光照貼圖。
//本例假設(shè)d3dDevice為指向IDirect3DDevice9接口的有效指針,
//且lptexLightMap為指向包含單色光照貼圖數(shù)據(jù)的紋理的有效指針。
//把光照貼圖設(shè)置為當(dāng)前紋理。
d3dDevice->SetTexture(0, lptexLightMap);
//設(shè)置顏色操作。
d3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//設(shè)置顏色操作的第一個(gè)參數(shù)。
d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1,D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE);
因?yàn)椴恢С帜繕?biāo)阿爾法混合的適配器一般來(lái)說(shuō)也不支持多重紋理混合,這個(gè)示例把光照貼圖設(shè)為第一張紋理,而這在所有三維加速卡上都是可用的。示例代碼先設(shè)置紋理混合層的顏色操作,讓紋理數(shù)據(jù)與圖元已有的顏色進(jìn)行混合,然后選擇第一張紋理和圖元已有的顏色作為輸入數(shù)據(jù)。
2,有色光照貼圖
如果應(yīng)用程序使用有色光照貼圖,那么通常會(huì)渲染得到更具真實(shí)感的三維場(chǎng)景。一張有色光照貼圖使用RGB數(shù)據(jù)存放光照信息。
以下C++示例代碼顯示了如何用RGB顏色數(shù)據(jù)進(jìn)行光照貼圖。
//本例假設(shè)d3dDevice為指向IDirect3DDevice9接口的有效指針,
//且lptexLightMap為指向包含單色光照貼圖數(shù)據(jù)的紋理的有效指針。
//把光照貼圖設(shè)為第一張紋理。
d3dDevice->SetTexture(0, lptexLightMap);
d3dDevice->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_MODULATE );
d3dDevice->SetTextureStageState( 0,D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_DIFFUSE );
這個(gè)示例先把光照貼圖設(shè)為第一張紋理,然后設(shè)置第一個(gè)混合層的狀態(tài),對(duì)輸入數(shù)據(jù)進(jìn)行調(diào)制(相乘),并把第一張紋理和圖元的當(dāng)前顏色用作調(diào)制操作的參數(shù)。
3,鏡面反射貼圖
在對(duì)發(fā)亮的物體——那些使用了高反射度材質(zhì)的物體——進(jìn)行光照計(jì)算時(shí)會(huì)產(chǎn)生鏡面反射高光。在一些情況下,由光照模塊產(chǎn)生的鏡面反射高光不夠精確,為了產(chǎn)生更吸引人的鏡面反射高光,許多Microsoft? Direct3D?應(yīng)用程序會(huì)給圖元使用鏡面反射光照貼圖。
要進(jìn)行鏡面反射光照貼圖,只需把鏡面反射光照貼圖與圖元的紋理相加,然后再和RGB光照貼圖進(jìn)行調(diào)制(與結(jié)果相乘)操作。
//以下C++示例代碼描述了這個(gè)過(guò)程。
//本例假設(shè)d3dDevice為指向IDirect3DDevice9接口的有效指針
//lptexBaseTexture為指向紋理的有效指針。
//lptexSpecLightMap為指向包含RGB鏡面反射光照貼圖數(shù)據(jù)的紋理的有效指針。
//lptexLightMap為指向包含RGB光照貼圖數(shù)據(jù)的紋理的有效指針。
//設(shè)置基本紋理。
d3dDevice->SetTexture(0, lptexBaseTexture );
//設(shè)置要對(duì)基本紋理執(zhí)行的操作及參數(shù)。
d3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
//設(shè)置鏡面反射光照貼圖。
d3dDevice->SetTexture(1, lptexSpecLightMap);
//設(shè)置要對(duì)鏡面反射光照貼圖執(zhí)行的操作及參數(shù)。
d3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD );
d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
//設(shè)置RGB光照貼圖。
d3dDevice->SetTexture(2, lptexLightMap);
//設(shè)置要對(duì)RGB光照貼圖執(zhí)行的操作及參數(shù)。
d3dDevice->SetTextureStageState(2,D3DTSS_COLOROP, D3DTOP_MODULATE);
d3dDevice->SetTextureStageState(2,D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(2,D3DTSS_COLORARG2, D3DTA_CURRENT );
4,漫反射光照貼圖
不光滑的表面在被光源照射時(shí)會(huì)顯示漫反射光。漫反射光的亮度取決于表面到光源的距離及表面法向與光源的方向向量間的夾角。通過(guò)光照計(jì)算(譯注:由光照模塊執(zhí)行)模擬漫反射光只能得到一般的效果。
應(yīng)用程序可以用光照貼圖模擬更為復(fù)雜的漫反射光照效果,只需在基本紋理的基礎(chǔ)上再加一張漫反射光照貼圖即可,如以下C++示例代碼所示。
//本例假設(shè)d3dDevice為指向IDirect3DDevice9接口的有效指針。
//lptexBaseTexture為指向紋理的有效指針。
//lptexLightMap為指向包含RGB光照貼圖數(shù)據(jù)的紋理的有效指針。
//設(shè)置基本紋理。
d3dDevice->SetTexture(0,lptexBaseTexture );
//設(shè)置要對(duì)基本紋理執(zhí)行的操作及參數(shù)。
d3dDevice->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_MODULATE );
d3dDevice->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_DIFFUSE );
//設(shè)置漫反射光照貼圖。
d3dDevice->SetTexture(1,lptexDiffuseLightMap );
//設(shè)置混合層 。
d3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE );
d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
上邊介紹了四種基本貼圖,下邊介紹幾種特效貼圖。
1,黑暗映射(Dark mapping)
黑暗映射實(shí)際上是把一張灰度圖作用于當(dāng)前的紋理中。采用調(diào)制的方式進(jìn)行混合,適合于微弱光照的情況進(jìn)行渲染。
2,黑暗貼圖動(dòng)畫(huà)
就是通過(guò)控制顯示的次數(shù)和顯示亮度的關(guān)系,分別調(diào)用三種不同的調(diào)制操作類(lèi)型來(lái)交替顯示,就可以達(dá)到黑暗貼圖動(dòng)畫(huà)的效果。適合于閃爍的黑暗環(huán)境。
3,細(xì)節(jié)紋理(Detail mapping)
采用一張細(xì)節(jié)紋理,將基礎(chǔ)紋理和細(xì)節(jié)紋理做addsigned操作,可以模擬粗糙的表面細(xì)節(jié)。
上述效果都不是固定的應(yīng)用,可以盡可能多的采用紋理類(lèi)型和紋理混合形式,最終實(shí)現(xiàn)更好的效果。