• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            永遠(yuǎn)也不完美的程序

            不斷學(xué)習(xí),不斷實(shí)踐,不斷的重構(gòu)……

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            好友鏈接

            最新評(píng)論

            紋理(講得比較詳細(xì)的文章)

            紋理是增強(qiáng)計(jì)算機(jī)生成的三維圖像的真實(shí)感的有力工具。Microsoft® Direct3D®支持廣泛的紋理特性,并使開發(fā)人員可以很方便地使用高級(jí)紋理技術(shù)。

            本節(jié)講述如何使用紋理。

            以下主題將更詳細(xì)地介紹另外的紋理功能。

            要提高性能,可以考慮使用動(dòng)態(tài)紋理。動(dòng)態(tài)紋理在每一幀都可以被鎖定,寫入及解鎖。更多信息請(qǐng)參閱使用動(dòng)態(tài)紋理


            紋理的基本概念


            早期計(jì)算機(jī)生成的三維圖像看起來(lái)往往像是發(fā)亮的塑料,雖然這在當(dāng)時(shí)也是比較先進(jìn)的,但是它們?nèi)狈Ω鞣N紋路——如磨損、裂痕、指紋和污漬等,而這些紋路會(huì)增加三維物體的真實(shí)感。近年來(lái),紋理已經(jīng)在開發(fā)人員中得到普及并作為增強(qiáng)計(jì)算機(jī)生成的三維圖像的真實(shí)感的工具。

            詞語(yǔ)“紋理”在日常使用中表示物體的光滑度或粗糙度,但是在計(jì)算機(jī)圖形學(xué)中,紋理指的是一張表示物體表面細(xì)節(jié)的位圖。

            因?yàn)?span>Direct3D中所有紋理都是位圖,所以可以把任何位圖貼到Direct3D圖元的表面。例如,應(yīng)用程序可以創(chuàng)建物體并使它們的表面看起來(lái)有木紋的樣式。可以把草、泥土和巖石等紋理貼在構(gòu)成山的圖元的表面,這樣就能得到看起來(lái)很真實(shí)的山坡。應(yīng)用程序也可以用紋理創(chuàng)建其它的效果,如:路邊的路標(biāo),懸崖邊的巖層,或是地面上的大理石。

            另外,Direct3D支持更高級(jí)的紋理技術(shù),如紋理混合(包含或不含透明度)和光照貼圖。更多信息請(qǐng)參閱紋理混合用紋理實(shí)現(xiàn)光照貼圖

            如果應(yīng)用程序創(chuàng)建一個(gè)HAL設(shè)備或軟件設(shè)備,那么可以使用8、16、24或是32位紋理。

            以下主題包含了更多的信息。


            紋理尋址模式


            Microsoft® Direct3D®應(yīng)用程序可以把紋理坐標(biāo)值賦給任何圖元的任何頂點(diǎn)。更多細(xì)節(jié),請(qǐng)參閱紋理坐標(biāo)。一般來(lái)說(shuō),應(yīng)用程序賦給頂點(diǎn)的u、v紋理坐標(biāo)值在0.0到1.0范圍內(nèi),閉區(qū)間。但是,通過(guò)把紋理坐標(biāo)值賦為此范圍外的值,應(yīng)用程序可以創(chuàng)建某些特殊紋理效果。

            通過(guò)設(shè)置紋理尋址模式,應(yīng)用程序可以控制當(dāng)紋理坐標(biāo)位于范圍[0.0, 1.0]外時(shí)希望Direct3D執(zhí)行何種操作。例如,應(yīng)用程序可以設(shè)置尋址模式,使紋理平鋪于圖元表面。下面的主題包含了更多的細(xì)節(jié)。

            Direct3D使應(yīng)用程序可以進(jìn)行紋理環(huán)繞,很重要的一點(diǎn)是要注意把紋理尋址模式設(shè)為D3DTADDRESS_WRAP與進(jìn)行紋理環(huán)繞并不相同。把紋理尋址模式設(shè)為D3DTADDRESS_WRAP會(huì)使源紋理的多個(gè)復(fù)本被貼到當(dāng)前圖元的表面,而啟用紋理環(huán)繞則會(huì)改變系統(tǒng)對(duì)貼有紋理的多邊形進(jìn)行光柵化的方式。更多細(xì)節(jié),請(qǐng)參閱紋理環(huán)繞

            啟用紋理環(huán)繞實(shí)際上使位于[0.0, 1.0]范圍之外的紋理坐標(biāo)無(wú)效,在這種情況下,對(duì)無(wú)效的紋理坐標(biāo)進(jìn)行光柵化操作將導(dǎo)致未定義的結(jié)果。當(dāng)啟用紋理環(huán)繞時(shí),不會(huì)使用紋理尋址模式,同時(shí)應(yīng)用程序應(yīng)該注意不要給出小于0.0或大于1.0的紋理坐標(biāo)。

            設(shè)置尋址模式

            應(yīng)用程序可以通過(guò)調(diào)用IDirect3DDevice9::SetSamplerState方法設(shè)置每個(gè)紋理層的紋理尋址模式,只需把紋理層的標(biāo)識(shí)作為第一個(gè)參數(shù),并把第二個(gè)參數(shù)設(shè)置為D3DSAMP_ADDRESSU,D3DSAMP_ADDRESSV或D3DSAMP_ADDRESSW,就可以分別改變u,v或w尋址模式。IDirect3DDevice9::SetSamplerState方法的第三個(gè)參數(shù)決定要設(shè)置的模式,這是一個(gè)D3DTEXTUREADDRESS枚舉類型值。要取得某一紋理層當(dāng)前的紋理尋址模式,只需調(diào)用IDirect3DDevice9::GetSamplerState方法,把第二個(gè)參數(shù)設(shè)置為D3DTEXTURESTAGESTATETYPE枚舉類型值,并把第三個(gè)參數(shù)設(shè)置為用于保存返回的尋址模式的變量的地址。

            設(shè)備限制

            雖然系統(tǒng)允許紋理坐標(biāo)位于閉區(qū)間0.0到1.0之外,但硬件限制通常會(huì)決定紋理坐標(biāo)究竟可以超出該范圍多少。當(dāng)應(yīng)用程序取得設(shè)備能力時(shí),渲染設(shè)備通過(guò)D3DCAPS9結(jié)構(gòu)的MaxTextureRepeat成員表明這個(gè)限制,這個(gè)成員的值描述了設(shè)備允許的紋理坐標(biāo)的全部范圍。例如,若這個(gè)值為128,則輸入的紋理坐標(biāo)必須保持在從-128.0到+128.0之間,若輸入頂點(diǎn)的紋理坐標(biāo)位于這個(gè)范圍外,則紋理坐標(biāo)是無(wú)效的。對(duì)自動(dòng)生成的紋理坐標(biāo)和由紋理坐標(biāo)變換得到的紋理坐標(biāo)也有同樣的限制。

            對(duì)MaxTextureRepeat的解釋同時(shí)還受D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE能力位的影響。若設(shè)置了這個(gè)能力位,則D3DCAPS9結(jié)構(gòu)的MaxTextureRepeat成員的值正如前面描述的一樣。但是,若沒有設(shè)置該能力位,則紋理循環(huán)的限制還取決于紋理坐標(biāo)尋址的那個(gè)紋理的大小。在這種情況下,MaxTextureRepeat必須除以當(dāng)前mipmap中最大紋理的大小。例如,若紋理大小為32,而MaxTextureRepeat的值為512,則實(shí)際有效的紋理坐標(biāo)的范圍是512/32 = 16,因此傳給該設(shè)備的紋理坐標(biāo)必須在范圍-16.0到+16.0之間。

            以下主題包含了更多有關(guān)紋理尋址的信息:        


            環(huán)繞紋理尋址模式


            環(huán)繞紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_WRAP成員表示,它會(huì)使Microsoft® Direct3D®在紋理坐標(biāo)的整數(shù)邊界重復(fù)使用該紋理。例如,設(shè)想應(yīng)用程序創(chuàng)建了一個(gè)方的圖元并把紋理坐標(biāo)指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設(shè)置為D3DTADDRESS_WRAP會(huì)使紋理在u和v方向都重復(fù)三次。

             

            這種紋理尋址模式的效果與鏡像紋理尋址模式有些相似,卻又明顯不同。更多信息,請(qǐng)參閱鏡像紋理尋址模式


            鏡像紋理尋址模式


            鏡像紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_MIRROR成員表示,它會(huì)使Microsoft® Direct3D®在紋理坐標(biāo)的整數(shù)邊界先對(duì)紋理進(jìn)行鏡像然后再重復(fù)使用。例如,設(shè)想應(yīng)用程序創(chuàng)建了一個(gè)方的圖元并把紋理坐標(biāo)指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設(shè)置為D3DTADDRESS_MIRROR會(huì)使紋理在u和v方向都重復(fù)三次,每一行和每一列的紋理都是相鄰行和列的紋理的鏡像。

             

            這種紋理尋址模式的效果與環(huán)繞紋理尋址模式有些相似,卻又明顯不同。更多信息,請(qǐng)參閱環(huán)繞紋理尋址模式


            截取紋理尋址模式


            截取紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_CLAMP成員表示,它會(huì)使Microsoft® Direct3D®把紋理坐標(biāo)截取到[0.0, 1.0]范圍內(nèi),也就是說(shuō),這種模式只應(yīng)用紋理一次,然后就重復(fù)使用紋理邊緣處像素的顏色。例如,設(shè)想應(yīng)用程序創(chuàng)建了一個(gè)方的圖元并把紋理坐標(biāo)指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設(shè)置為D3DTADDRESS_CLAMP會(huì)使紋理只被應(yīng)用一次,列的頂端和行的末端處像素的顏色被相應(yīng)地延伸至圖元的頂端和右邊。

            下圖描繪了截取尋址模式。

             


            邊框顏色紋理尋址模式


            邊框顏色紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_BORDER成員表示,該尋址模式會(huì)使Microsoft® Direct3D®對(duì)于位于[0.0, 1.0]范圍之外的紋理坐標(biāo)使用一個(gè)被稱為邊框顏色的指定顏色。

            下圖描繪了邊框顏色紋理尋址模式,這里應(yīng)用程序指定紅色為紋理的邊框顏色。

             

            應(yīng)用程序可以通過(guò)調(diào)用IDirect3DDevice9::SetSamplerState方法設(shè)置邊框顏色。在調(diào)用時(shí)要把第一個(gè)參數(shù)設(shè)為想要設(shè)置的紋理層的標(biāo)識(shí),把第二個(gè)參數(shù)設(shè)為D3DSAMP_BORDERCOLOR紋理層狀態(tài)值,并把第三個(gè)參數(shù)設(shè)為以RGBA形式表示的新的邊框顏色。


            無(wú)效紋理區(qū)域


            通過(guò)給紋理指定無(wú)效區(qū)域,應(yīng)用程序可以對(duì)需要復(fù)制紋理的哪些子集進(jìn)行優(yōu)化,只有那些被標(biāo)記為無(wú)效的區(qū)域才會(huì)被IDirect3DDevice9::UpdateTexture方法更新。當(dāng)創(chuàng)建紋理時(shí),整個(gè)紋理被標(biāo)記為無(wú)效的。只有以下幾種操作可以改變紋理的無(wú)效狀態(tài)。

            • 給一個(gè)紋理添加一個(gè)無(wú)效區(qū)域。
            • 鎖定紋理中的一些區(qū)域。此操作會(huì)把被鎖定的區(qū)域添加到無(wú)效區(qū)域中,如果應(yīng)用程序明確知道哪些是真正的無(wú)效區(qū)域,那么也可以關(guān)閉對(duì)無(wú)效區(qū)域的自動(dòng)更新。
            • 將紋理作為目標(biāo)表面進(jìn)行更新的話會(huì)把整個(gè)紋理標(biāo)記為無(wú)效的。
            • 對(duì)紋理調(diào)用IDirect3DDevice9::UpdateTexture方法會(huì)清除該紋理的所有無(wú)效區(qū)域。
            • 為了得到設(shè)備上下文(device context)而調(diào)用IDirect3DDevice9::GetDC

            對(duì)于mipmap紋理而言,無(wú)效區(qū)域被設(shè)在最高一級(jí)的紋理上,為了最小化對(duì)mipmap紋理中每一級(jí)的紋理更新所需復(fù)制的字節(jié)數(shù),IDirect3DDevice9::UpdateTexture方法可以擴(kuò)展無(wú)效區(qū)域并沿mipmap鏈更新子紋理。注意子級(jí)中無(wú)效區(qū)域的紋理坐標(biāo)被向上舍入,也就是說(shuō),它們的小數(shù)部分被向上取整到紋理中最近的像素。

            因?yàn)槊糠N類型的紋理具有不同類型的無(wú)效區(qū)域,所以每種類型的紋理都有相應(yīng)的方法表示無(wú)效區(qū)域。二維紋理使用矩形,立體紋理使用立方體。

            • IDirect3DCubeTexture9::AddDirtyRect
            • IDirect3DTexture9::AddDirtyRect
            • IDirect3DVolumeTexture9::AddDirtyBox

            把以上方法的pDirtyRectpDirtyBox參數(shù)設(shè)置為NULL會(huì)擴(kuò)大無(wú)效區(qū)域并使之覆蓋整個(gè)紋理。

            每種鎖定方法都有D3DLOCK_NO_DIRTY_UPDATE標(biāo)志,使用這個(gè)標(biāo)志可以防止對(duì)紋理無(wú)效區(qū)域的改變。更多信息,請(qǐng)參閱鎖定資源

            如果在鎖定操作時(shí)可以得到已改變區(qū)域的完整集合,那么應(yīng)用程序應(yīng)該使用D3DLOCK_NO_DIRTY_UPDATE標(biāo)志。注意,對(duì)紋理一個(gè)的子級(jí)的鎖定或復(fù)制操作(也就是說(shuō),未對(duì)紋理的最高一級(jí)進(jìn)行鎖定或復(fù)制操作)不會(huì)更新該紋理的無(wú)效區(qū)域。當(dāng)應(yīng)用程序鎖定了紋理的子級(jí)而沒有鎖定紋理的最高一級(jí)時(shí),它同樣有責(zé)任對(duì)無(wú)效區(qū)域進(jìn)行更新。


            紋理調(diào)色板


            Microsoft DirectX® 9.0中的Microsoft® Direct3D®通過(guò)一組與IDirect3DDevice9對(duì)象相關(guān)聯(lián)的256色調(diào)色板支持調(diào)色板紋理(paletted texture)。通過(guò)調(diào)用IDirect3DDevice9::SetCurrentTexturePalette方法可以設(shè)置當(dāng)前調(diào)色板。當(dāng)前調(diào)色板用于對(duì)所有已激活的紋理層中的所有調(diào)色板紋理進(jìn)行顏色轉(zhuǎn)換。IDirect3DDevice9::SetPaletteEntries方法可以更新調(diào)色板中的全部256個(gè)顏色項(xiàng)。每個(gè)顏色項(xiàng)都是一個(gè)用D3DFMT_A8R8G8B8格式表示的PALETTEENTRY結(jié)構(gòu),默認(rèn)值為0xFFFFFFFF。

            IDirect3DDevice9的調(diào)色板包含了一個(gè)阿爾法通道。若設(shè)備設(shè)置了D3DPTEXTURECAPS_ALPHAPALETTE能力位,則表示該設(shè)備支持調(diào)色板阿爾法,并可以使用該阿爾法通道。當(dāng)紋理格式不含阿爾法通道時(shí),就使用調(diào)色板阿爾法通道。若設(shè)備不支持調(diào)色板阿爾法,同時(shí)紋理格式也不含阿爾法通道,則使用0xFF作為阿爾法值。

            系統(tǒng)中最多可以有65,536個(gè)調(diào)色板。因?yàn)檎{(diào)色板占用的內(nèi)存資源與應(yīng)用程序引用到的最大的調(diào)色板編號(hào)成正比,所以最好使用從零開始且連續(xù)的編號(hào)。


            紋理坐標(biāo)


            大多數(shù)紋理,如位圖,都是一個(gè)存放顏色值的二維數(shù)組,但立方體環(huán)境貼圖除外,具體細(xì)節(jié)請(qǐng)參閱立方體環(huán)境貼圖。數(shù)組中的每個(gè)顏色值被稱為texel。每個(gè)texel在紋理中有唯一的地址,可以認(rèn)為這個(gè)地址是行和列的編號(hào),它們分別被標(biāo)記為u和v。

            紋理坐標(biāo)位于紋理空間中,也就是說(shuō),它們相對(duì)于紋理中的位置(0,0)點(diǎn)。當(dāng)把紋理貼到三維空間中圖元的表面時(shí),紋理的texel必須先被映射到對(duì)象坐標(biāo)系,然后再變換到屏幕坐標(biāo)系,或像素的位置。

            Texel映射到屏幕空間

            Microsoft® Direct3D®直接把紋理中的texel映射到屏幕空間,這樣就省略了中間步驟并極大地提高了效率。這個(gè)映射的過(guò)程實(shí)際上是一個(gè)反向映射,也就是說(shuō),系統(tǒng)根據(jù)每個(gè)像素在屏幕空間中的位置計(jì)算該像素在紋理空間中相應(yīng)的texel的位置,然后對(duì)位于該點(diǎn)或該點(diǎn)附近的紋理顏色進(jìn)行取樣。取樣的過(guò)程被稱為紋理過(guò)濾。更多信息請(qǐng)參閱紋理過(guò)濾

            紋理中每個(gè)texel的位置可以用它的texel坐標(biāo)表示。但是為了把texel貼到圖元表面,Direct3D需要所有的紋理中的texel具有相同的地址范圍,所以Direct3D使用了一種通用的尋址方法。在這種尋址方法中,所有texel的地址都在閉區(qū)間0.0到1.0內(nèi)。Direct3D用u,v值表示紋理坐標(biāo),這和用x,y坐標(biāo)表示二維笛卡爾坐標(biāo)系非常相似。從技術(shù)上講,系統(tǒng)事實(shí)上可以處理0.0到1.0范圍外的紋理坐標(biāo),系統(tǒng)根據(jù)應(yīng)用程序設(shè)置的紋理尋址模式來(lái)進(jìn)行此類處理。更多信息,請(qǐng)參閱紋理尋址模式

            采用這種方法的結(jié)果是相同的紋理地址在不同的紋理中會(huì)映射到不同的texel坐標(biāo)。在下圖中,正在使用的紋理地址是(0.5,1.0)。但是,因?yàn)榧y理的大小不同,所以該紋理地址映射到不同的texel。左邊紋理的大小為5x5,紋理地址(0.5,1.0)映射到texel (2,4)。右邊紋理的大小為7x7,紋理地址(0.5,1.0)映射到texel (3,6)。

             

            下圖描述了一個(gè)經(jīng)簡(jiǎn)化的texel映射過(guò)程。這個(gè)示例顯然非常簡(jiǎn)單,要了解更多細(xì)節(jié)信息,請(qǐng)參閱直接把Texel映射到像素

             

            本例中,圖的左邊顯示了一個(gè)被理想化為正方形色塊的像素。像素四角的地址被映射到對(duì)象空間中的三維圖元,因?yàn)槿S場(chǎng)景中圖元的形狀及觀察角度的不同,像素的形狀常常會(huì)被扭曲。像素四角所對(duì)應(yīng)圖元表面的區(qū)域然后被映射到紋理空間,這個(gè)映射過(guò)程會(huì)再次扭曲像素的形狀。像素的最終顏色根據(jù)像素映射的區(qū)域所覆蓋的texel計(jì)算得到。通過(guò)設(shè)置紋理過(guò)濾方法,應(yīng)用程序可以控制讓Direct3D使用何種方法計(jì)算像素顏色。更多信息,請(qǐng)參閱紋理過(guò)濾

            應(yīng)用程序可以直接給頂點(diǎn)指定紋理坐標(biāo),這使應(yīng)用程序可以控制把紋理的哪些部分貼到圖元上。例如,設(shè)想應(yīng)用程序創(chuàng)建了一個(gè)與下面的紋理大小完全相同的圖元,本例中,如果應(yīng)用程序希望把整張紋理貼到整面墻上,那么應(yīng)用程序給圖元的頂點(diǎn)指定的紋理坐標(biāo)就應(yīng)該是(0.0,0.0), (1.0,0.0), (1.0,1.0), 和 (0.0,1.0)。

             

            如果應(yīng)用程序決定把墻的高度減半,應(yīng)用程序仍可以把整張貼圖貼到稍小的墻上,但這會(huì)擠壓紋理并使之扭曲,或者應(yīng)用程序也可以重新指定紋理坐標(biāo),使Direct3D使用紋理的下半部分。

            如果應(yīng)用程序?yàn)榱税鸭y理貼到稍小的墻上而決定擠壓或拉伸紋理,那么應(yīng)用程序使用的紋理過(guò)濾方法會(huì)影響最終圖像的質(zhì)量。更多信息,請(qǐng)參閱紋理過(guò)濾

            如果應(yīng)用程序決定重新指定紋理坐標(biāo)并使Direct3D使用紋理的下半部分,那么在本例中應(yīng)用程序給圖元的頂點(diǎn)指定的紋理坐標(biāo)應(yīng)該是(0.0,0.5), (1.0,0.5), (1.0,1.0), 和 (0.0,1.0),這樣Direct3D就會(huì)把紋理的下半部分貼到墻上。

            頂點(diǎn)的紋理坐標(biāo)有可能大于1.0,如果應(yīng)用程序給頂點(diǎn)指定的紋理坐標(biāo)不在閉區(qū)間0.0到1.0范圍內(nèi),那么應(yīng)用程序還應(yīng)該設(shè)置紋理尋址模式。更多信息,請(qǐng)參閱紋理尋址模式

            紋理坐標(biāo)和紋理層

            紋理坐標(biāo)通過(guò)紋理層與紋理聯(lián)系在一起。紋理通過(guò)SetTexture(stageIndex, pTextre) 被設(shè)定到某一紋理層。請(qǐng)參閱IDirect3DDevice9::SetTexture

            一個(gè)彈性頂點(diǎn)格式碼最多可以定義八組紋理坐標(biāo),紋理坐標(biāo)數(shù)據(jù)由用戶在頂點(diǎn)數(shù)據(jù)中提供,數(shù)據(jù)通過(guò)索引值0到7來(lái)引用。最多可以有八個(gè)紋理混合層,一張紋理通過(guò)SetTexture(stageIndex, pTexture)與某一紋理層聯(lián)系在一起。

            完成以上操作后,任意一組紋理坐標(biāo)可以被任意一紋理層使用。每一組紋理坐標(biāo)通過(guò)SetTextureStageState(stageIndex, D3DTSS_TEXCOORDINDEX, textureCoordinateIndex)與某一紋理層聯(lián)系在一起。請(qǐng)參閱IDirect3DDevice9::SetTextureStageState。通過(guò)這種方法,可以設(shè)置紋理混合層使它們使用任意一張紋理和任意一組紋理坐標(biāo)。多個(gè)紋理層可以使用同一張紋理,或同一組紋理坐標(biāo)。

            以下主題包含了更多的信息。


            直接把Texel映射到像素


            應(yīng)用程序經(jīng)常需要把紋理貼到幾何體上并使texel直接映射到屏幕上的像素。例如,以一個(gè)需要在紋理中顯示文本的應(yīng)用程序?yàn)槔瑸榱耸辜y理中的文本能清晰地顯示,應(yīng)用程序需要以某種方式確保映射到幾何體上的texel不受紋理過(guò)濾的影響。如果無(wú)法保證這一點(diǎn),那么得到的圖像通常會(huì)是模糊的,如果紋理過(guò)濾方法為最近點(diǎn)取樣,那么可能會(huì)產(chǎn)生粗糙邊緣。

            為了統(tǒng)一像素和紋理取樣,并同時(shí)支持圖像和紋理過(guò)濾,Microsoft® Direct3D®的像素和紋理取樣規(guī)則經(jīng)過(guò)了精心定義,但這也使得把紋理中的texel直接映射到屏幕上的像素成為一個(gè)相當(dāng)有意義卻又艱難的挑戰(zhàn)。要戰(zhàn)勝這個(gè)挑戰(zhàn),需要透徹地理解Direct3D如何把用浮點(diǎn)數(shù)表示的紋理坐標(biāo)映射到光柵化器使用的整數(shù)像素坐標(biāo)。

            為了把用浮點(diǎn)數(shù)表示的紋理坐標(biāo)映射到texel地址,Direct3D執(zhí)行下面的計(jì)算。

             

            在這些公式中,TxTy為水平/垂直方向的輸出texel坐標(biāo),uv為頂點(diǎn)提供的水平/垂直方向的紋理坐標(biāo)。MxMy元素表示當(dāng)前mipmap級(jí)水平/垂直方向的texel的數(shù)量。本節(jié)剩余部分將主要討論在水平方向上從texel到像素的映射,垂直方向上的映射與水平方向完全相同。

            把紋理坐標(biāo)0.0和1.0代入以上公式,會(huì)使紋理坐標(biāo)0.0映射到本次紋理迭代的第一個(gè)texel和上次紋理迭代的最后一個(gè)texel的中間,紋理坐標(biāo)1.0會(huì)被映射到本次紋理迭代的最后一個(gè)texel和下次紋理迭代的第一個(gè)texel的中間。對(duì)于一個(gè)寬度為4的迭代紋理,在mipmap的第0級(jí),下圖顯示了系統(tǒng)把坐標(biāo)0.0和1.0映射到哪里。

             

            理解了這種映射方式,應(yīng)用程序只需給幾何體的屏幕空間坐標(biāo)加上一個(gè)偏移量,就可以強(qiáng)制系統(tǒng)把每個(gè)texel映射到相應(yīng)的像素。例如,要繪制一個(gè)四邊形并使前面的紋理中的每個(gè)texel一一映射到屏幕上唯一的像素,應(yīng)用程序必須使幾何體坐標(biāo)覆蓋所有像素,并使每個(gè)texel的中心正好對(duì)應(yīng)每個(gè)像素的中心,這樣得到的結(jié)果就是應(yīng)用程序常要追求的一對(duì)一映射。

            為了把寬度為4的紋理映射到像素坐標(biāo)0到3,可以用兩個(gè)三角形繪制一個(gè)四邊形,三角形在屏幕空間的坐標(biāo)為-0.5到3.5,紋理坐標(biāo)為0.0到1.0。以屏幕空間坐標(biāo)為0.0的像素為例,因?yàn)?.0與第一個(gè)頂點(diǎn),位于屏幕空間-0.5,相距半個(gè)像素,而總的寬度為4.0,迭代后的紋理坐標(biāo)為0.125(譯注:每個(gè)像素的寬度為0.25=1/4,半個(gè)像素的寬度為0.125),然后用紋理的大小,此處為4,對(duì)紋理坐標(biāo)進(jìn)行縮放,得到的結(jié)果坐標(biāo)為0.5。再減去偏移量0.5即得到紋理地址為0.0,該地址完全對(duì)應(yīng)于貼圖中的第一個(gè)texel。

            再概括一下,紋理坐標(biāo)覆蓋紋理貼圖的兩邊,并平均分布在它們之間。下圖顯示了這種映射,紋理的寬度為4。

             

            系統(tǒng)以相同的方法歸一化像素坐標(biāo)和紋理坐標(biāo),因此,如果頂點(diǎn)與要渲染的像素重疊,且頂點(diǎn)使用的紋理坐標(biāo)為0.0和1.0,那么像素和頂點(diǎn)就可以對(duì)齊。如果它們的大小相同且排列整齊,那texel和像素就可以完全對(duì)應(yīng),如下圖所示。

             


            紋理坐標(biāo)的格式


            Microsoft® Direct3D®中的紋理坐標(biāo)可以包含一個(gè)、兩個(gè)、三個(gè)或四個(gè)用浮點(diǎn)數(shù)表示的元素,用來(lái)尋址不同大小的紋理。一維紋理——紋理表面的大小為1xn個(gè)texel——通過(guò)一個(gè)紋理坐標(biāo)尋址。最常見的情況是二維紋理,通過(guò)兩個(gè)被稱為uv的紋理坐標(biāo)尋址。Direct3D支持兩種三維紋理,立方體環(huán)境貼圖立體貼圖。立方體環(huán)境貼圖不是真正三維的,但使用一個(gè)三元素向量尋址。更多細(xì)節(jié),請(qǐng)參閱立方體環(huán)境貼圖

            頂點(diǎn)格式中所述,應(yīng)用程序把紋理坐標(biāo)編碼在頂點(diǎn)格式中。頂點(diǎn)格式可以包含多組紋理坐標(biāo)。可以用FVF碼D3DFVF_TEX0到D3DFVF_TEX8描述不包含紋理坐標(biāo)或最多可包含八組紋理坐標(biāo)的頂點(diǎn)格式。

            每組紋理坐標(biāo)可以有一到四個(gè)元素。D3DFVF_TEXTUREFORMAT1到D3DFVF_TEXTUREFORMAT4標(biāo)志用來(lái)描述一組紋理坐標(biāo)中元素的個(gè)數(shù),但這些標(biāo)志不能直接使用,D3DFVF_TEXCOORDSIZEn系列宏使用這些標(biāo)志創(chuàng)建位掩碼,用來(lái)在頂點(diǎn)格式中描述某組紋理坐標(biāo)中元素的個(gè)數(shù)。這些宏帶一個(gè)參數(shù),表示要設(shè)置元素個(gè)數(shù)的那組紋理坐標(biāo)的索引值。以下示例代碼顯示了如何使用這些宏。

            // 這個(gè)頂點(diǎn)格式包含兩組紋理坐標(biāo)。第一組(索引為0)包含2個(gè)元素,

            // 第二組包含一個(gè)元素。頂點(diǎn)格式描述應(yīng)該是:

            //     dwFVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 |

            //             D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE1(1);

            //

            typedef struct CVF

            {

                D3DVECTOR position;

                D3DVECTOR normal;

                D3DCOLOR diffuse;

                float     u, v;   // 第一組紋理坐標(biāo),二維

                float     t;      // 第二組紋理坐標(biāo),一維

            } CustomVertexFormat;

            注意 除了立方體貼圖和立體貼圖外,光柵化器無(wú)法使用兩個(gè)以上的元素尋址紋理。應(yīng)用程序最多可以提供三個(gè)元素給一組紋理坐標(biāo),但只有當(dāng)紋理是立方體貼圖或立體貼圖,或使用了D3DTTFF_PROJECTED紋理變換標(biāo)志時(shí)多余的元素才會(huì)被用到。D3DTTFF_PROJECTED標(biāo)志會(huì)使光柵化器把前兩個(gè)元素除以第三個(gè)(或第n個(gè))元素。更多信息,請(qǐng)參閱紋理坐標(biāo)變換


            紋理坐標(biāo)的處理


            下圖顯示了紋理坐標(biāo)從數(shù)據(jù)源,經(jīng)過(guò)處理然后到達(dá)光柵化器的流程。

             

            系統(tǒng)可以從兩個(gè)數(shù)據(jù)源得到紋理坐標(biāo)。對(duì)于某一層紋理,應(yīng)用程序可以使用包含在頂點(diǎn)格式(D3DFVF_TEX1到D3DFVF_TEX8)中的紋理坐標(biāo),也可以使用Microsoft® Direct3D®自動(dòng)生成的紋理坐標(biāo)。對(duì)于后一種情況,請(qǐng)參閱自動(dòng)生成的紋理坐標(biāo)。如果當(dāng)前紋理層的D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀態(tài)為D3DTTFF_DISABLE(默認(rèn)值),那么系統(tǒng)不會(huì)對(duì)輸入的紋理坐標(biāo)進(jìn)行變換。如果D3DTSS_TEXTURETRANSFORMFLAGS為任何其它值,那么系統(tǒng)會(huì)用該紋理層的變換矩陣對(duì)該輸入紋理坐標(biāo)進(jìn)行變換。

            D3DTEXTURETRANSFORMFLAGS枚舉類型定義了D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀態(tài)的有效值。除了D3DTTFF_DISABLE標(biāo)志不進(jìn)行紋理坐標(biāo)變換外,該枚舉類型值用于設(shè)定系統(tǒng)傳送至光柵化器的輸出坐標(biāo)的數(shù)量。D3DTTFF_COUNT1到D3DTTFF_COUNT4標(biāo)志告訴系統(tǒng)把輸出紋理坐標(biāo)中的一個(gè)、兩個(gè)、三個(gè)或四個(gè)元素傳送至光柵化器。

            D3DTTFF_PROJECTED標(biāo)志有些特別:它告訴系統(tǒng)紋理坐標(biāo)將用于經(jīng)過(guò)投影的紋理。把D3DTTFF_PROJECTED標(biāo)志與D3DTEXTURETRANSFORMFLAGS的其它成員一起使用可以告訴系統(tǒng)在光柵化操作前把所有元素除以最后一個(gè)元素。例如,當(dāng)顯式使用三元素紋理坐標(biāo),或紋理變換產(chǎn)生三元素紋理坐標(biāo)時(shí),應(yīng)用程序可以同時(shí)使用D3DTTFF_COUNT3和D3DTTFF_PROJECTED標(biāo)志,這會(huì)使光柵化器把前兩個(gè)元素除以最后一個(gè)元素,并得到尋址二維紋理所需的二維紋理坐標(biāo)。

            注意 除了立方體貼圖和立體貼圖外,光柵化器無(wú)法使用多于兩個(gè)元素的紋理坐標(biāo)。如果應(yīng)用程序提供的元素比尋址當(dāng)前紋理層所需的多,那么多余的元素會(huì)被忽略。當(dāng)把二維紋理坐標(biāo)用于一維紋理時(shí)也是這樣。

            以下主題包含了更多的信息。


            自動(dòng)生成的紋理坐標(biāo)


            系統(tǒng)可以使用經(jīng)過(guò)變換的攝像機(jī)空間中的位置或頂點(diǎn)的法向量作為紋理坐標(biāo),也可以計(jì)算用于尋址立方體貼圖的三元素向量。與應(yīng)用程序在頂點(diǎn)數(shù)據(jù)中明確給出的紋理坐標(biāo)一樣,應(yīng)用程序可以使用自動(dòng)生成的紋理坐標(biāo)作為紋理變換的輸入。

            通過(guò)無(wú)需在頂點(diǎn)格式中顯式地給出紋理坐標(biāo),自動(dòng)生成的紋理坐標(biāo)可以顯著降低幾何數(shù)據(jù)所需的帶寬。在許多情況下,系統(tǒng)生成的紋理坐標(biāo)可以和紋理變換一起使用以生成特效。當(dāng)然這只是一種特殊用途,大多數(shù)情況下應(yīng)用程序還是要用顯式給出的紋理坐標(biāo)。

            設(shè)定自動(dòng)生成的紋理坐標(biāo)

            C++應(yīng)用程序用D3DTSS_TEXCOORDINDEX紋理層狀態(tài)(來(lái)自D3DTEXTURESTAGESTATETYPE)控制系統(tǒng)如何產(chǎn)生紋理坐標(biāo)。

            一般來(lái)說(shuō),這個(gè)狀態(tài)告訴系統(tǒng)使用編碼在頂點(diǎn)格式中的某組特定的紋理坐標(biāo)。當(dāng)應(yīng)用程序給這個(gè)狀態(tài)指定的值包含D3DTSS_TCI_CAMERASPACENORMAL,D3DTSS_TCI_CAMERASPACEPOSITION,或D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR標(biāo)志時(shí),系統(tǒng)執(zhí)行的操作會(huì)完全不同。如果使用了這些標(biāo)志中的任意一個(gè),那么紋理層會(huì)忽略在頂點(diǎn)格式中的給出的紋理坐標(biāo),并優(yōu)先使用系統(tǒng)生成的坐標(biāo)。下表列出了每個(gè)標(biāo)志的意義。

            • D3DTSS_TCI_CAMERASPACENORMAL

            使用變換到攝像機(jī)空間的頂點(diǎn)法向作為輸入紋理坐標(biāo)。

            • D3DTSS_TCI_CAMERASPACEPOSITION

            使用變換到攝像機(jī)空間的頂點(diǎn)位置作為輸入紋理坐標(biāo)。

            • D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

            使用變換到攝像機(jī)空間的反射向量作為輸入紋理坐標(biāo)。反射向量根據(jù)輸入頂點(diǎn)位置和法向量計(jì)算得到。

            前面的這些標(biāo)志是互斥的。如果應(yīng)用程序指定了其中一個(gè)標(biāo)志,那么應(yīng)用程序還可以指定一個(gè)索引值,系統(tǒng)用這個(gè)索引值決定紋理環(huán)繞模式。

            以下示例代碼顯示了如何在C++應(yīng)用程序中使用這些標(biāo)志。

            /*

             * 在本例中,d3dDevice變量為指向IDirect3DDevice9接口的有效指針。

             *

             * 在當(dāng)前紋理層使用頂點(diǎn)位置(攝像機(jī)空間)作為輸入紋理坐標(biāo),

             * 紋理環(huán)繞模式在D3DRENDERSTATE_WRAP1渲染狀態(tài)中設(shè)置。

             */

            d3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX,

                                               D3DTSS_TCI_CAMERASPACEPOSITION | 1 );

            自動(dòng)生成的紋理坐標(biāo)在作為紋理坐標(biāo)變換的輸入,或使應(yīng)用程序無(wú)需計(jì)算用于尋址立方體環(huán)境貼圖的三元素向量時(shí)最為有用。

            球形貼圖使用一張預(yù)計(jì)算的(在建模時(shí))紋理貼圖,該貼圖包含了一個(gè)反光的球體反射的整個(gè)環(huán)境。Microsoft® Direct3D®有一個(gè)紋理坐標(biāo)自動(dòng)生成特性,使用了D3DTSS_TCI_CAMERASPACENORMAL渲染狀態(tài),該特性會(huì)取得變換到攝像機(jī)空間的頂點(diǎn)法向,并對(duì)它進(jìn)行紋理變換生成紋理坐標(biāo)。更多信息請(qǐng)參閱Sphere Map示例

            相關(guān)主題


            紋理坐標(biāo)變換


            Microsoft® Direct3D®設(shè)備可以用一個(gè)4x4矩陣對(duì)頂點(diǎn)的紋理坐標(biāo)進(jìn)行變換。系統(tǒng)使用相同的方法對(duì)紋理坐標(biāo)和幾何體進(jìn)行變換。任何變換(縮放、旋轉(zhuǎn)、投影、shear或這些變換的組合)可以用一個(gè)4x4矩陣完成。

            注意 Direct3D不改變經(jīng)過(guò)變換和光照處理的頂點(diǎn),因此,使用經(jīng)過(guò)變換和光照處理的頂點(diǎn)的應(yīng)用程序無(wú)法讓Direct3D變換頂點(diǎn)的紋理坐標(biāo)。

            支持硬件變換和光照的設(shè)備(TnLHAL設(shè)備)也會(huì)對(duì)紋理坐標(biāo)變換進(jìn)行硬件加速。如果設(shè)備不支持硬件變換和光照,那么Direct3D會(huì)使用幾何流水線中與平臺(tái)相關(guān)的優(yōu)化進(jìn)行紋理坐標(biāo)變換。

            紋理坐標(biāo)變換非常有用,它在生成特效的同時(shí)避免了對(duì)幾何體紋理坐標(biāo)的直接修改。應(yīng)用程序可以使用簡(jiǎn)單的平移或旋轉(zhuǎn)矩陣給物體表面的紋理生成動(dòng)畫效果,也可以對(duì)Direct3D自動(dòng)生成的紋理坐標(biāo)進(jìn)行變換,這樣可以簡(jiǎn)化并可能加速如投影紋理和動(dòng)態(tài)光照貼圖等高級(jí)特效。另外,在多層紋理中,應(yīng)用程序也可以用紋理坐標(biāo)變換重復(fù)使用某一組紋理坐標(biāo)并將之用于多種用途。

            設(shè)置及取得紋理坐標(biāo)變換的信息

            和應(yīng)用程序用于變換幾何體的矩陣一樣,應(yīng)用程序可以通過(guò)IDirect3DDevice9::SetTransformIDirect3DDevice9::GetTransform方法設(shè)置和取得紋理坐標(biāo)變換的信息。在調(diào)用這些方法時(shí),用D3DTRANSFORMSTATETYPE枚舉類型的成員D3DTS_TEXTURE0到D3DTS_TEXTURE7標(biāo)識(shí)紋理層0到7。

            以下示例代碼設(shè)置了一個(gè)矩陣,對(duì)紋理層0的紋理坐標(biāo)進(jìn)行變換。

            // 本例中,假設(shè)d3dDevice變量為指向IDirect3DDevice9接口的有效指針。

             

            D3DMATRIX matTrans = D3DXMatrixIdentity( NULL );

             

            // 為希望的變換設(shè)置矩陣。

            d3dDevice->SetTransform( D3DTS_TEXTURE0, &matTrans );

            啟用紋理坐標(biāo)變換

            D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀態(tài)控制對(duì)紋理坐標(biāo)的變換。這個(gè)紋理層狀態(tài)的值由D3DTEXTURETRANSFORMFLAGS枚舉類型定義。

            當(dāng)D3DTSS_TEXTURETRANSFORMFLAGS為D3DTTFF_DISABLE(默認(rèn)值)時(shí),紋理坐標(biāo)變換被禁用。假設(shè)紋理層0的紋理坐標(biāo)變換已啟用,以下代碼將之禁用。

            // 本例中,假設(shè)d3dDevice變量為指向IDirect3DDevice9接口的有效指針。

             

            d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                             D3DTTFF_DISABLE );

            D3DTEXTURETRANSFORMFLAGS定義的其它值用于啟用紋理坐標(biāo)變換,并控制把結(jié)果紋理坐標(biāo)中的幾個(gè)元素傳送到光柵化器。以下為示例代碼。

            // 本例中,假設(shè)d3dDevice變量為指向IDirect3DDevice9接口的有效指針。

             

            d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                             D3DTTFF_COUNT2 );

            D3DTTFF_COUNT2值告訴系統(tǒng)對(duì)紋理層0進(jìn)行變換,然后把得到的紋理坐標(biāo)的前兩個(gè)元素傳送給光柵化器。

            D3DTTFF_PROJECTED紋理變換標(biāo)志表示用于投影紋理的坐標(biāo)。當(dāng)這個(gè)標(biāo)志被設(shè)置時(shí),光柵化器會(huì)把傳入的元素除以最后一個(gè)元素。以下為示例代碼。

            // 本例中,假設(shè)d3dDevice變量為指向IDirect3DDevice9接口的有效指針。

             

            d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                             D3DTTFF_COUNT3 | D3DTTFF_PROJECTED );

            本例告訴系統(tǒng)傳送三個(gè)紋理坐標(biāo)元素到光柵化器。光柵化器把前兩個(gè)元素除以第三個(gè)元素,得到尋址紋理所需的二維紋理坐標(biāo)。


            特效


            本主題包含了可以用紋理坐標(biāo)處理實(shí)現(xiàn)的特效。

            給建模表面的紋理生成動(dòng)畫效果(通過(guò)變換或旋轉(zhuǎn))

            • 在頂點(diǎn)格式中定義一組二維紋理坐標(biāo)。

            ·                // 使用單紋理,二維紋理坐標(biāo)。這個(gè)位掩碼會(huì)根據(jù)需要

            ·                // 被擴(kuò)展為包含位置,法向和顏色信息。

            DWORD dwFVFTex = D3FVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);

            • 設(shè)定光柵化器,使之使用二維紋理坐標(biāo)。

            SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

            • 定義并設(shè)置合適的紋理坐標(biāo)變換矩陣

            // M為要設(shè)置的D3DMATRIX,用于在U和V方向?qū)y理坐標(biāo)進(jìn)行變換。

            //      1   0 0 0

            //      0   1 0 0

            //      du dv 1 0 (du和dv每幀都會(huì)改變)

            //      0   0 0 1

             

            D3DMATRIX M = D3DXMatrixIdentity(); // 在d3dutil.h中聲明

            M._31 = du;

            M._32 = dv;

            把紋理坐標(biāo)作為建模在攝像機(jī)空間中的位置的線性函數(shù)創(chuàng)建

            • D3DTSS_TCI_CAMERASPACEPOSITION標(biāo)志告訴系統(tǒng)使用頂點(diǎn)在攝像機(jī)空間中的位置作為紋理變換的輸入。

            ·                // 為了節(jié)省帶寬,輸入頂點(diǎn)沒有紋理坐標(biāo)。三個(gè)紋理坐標(biāo)用頂點(diǎn)在

            ·                // 攝像機(jī)空間中的位置(x, y, z)產(chǎn)生。

            SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);

            • 告訴光柵化器使用二維紋理坐標(biāo)。

            ·                // 使用了兩個(gè)輸出紋理坐標(biāo)。

            SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

            • 定義并設(shè)置生成線性函數(shù)的矩陣。

            // 把紋理坐標(biāo)作為線性函數(shù)創(chuàng)建,這樣的話:

            //      u = Ux*x + Uy*y + Uz*z + Uw

            //      v = Vx*x + Vy*y + Vz*z + Vw

            // 這種情況下矩陣M為:

            //      Ux Vx 0 0

            //      Uy Vy 0 0

            //      Uz Vz 0 0

            //      Uw Vw 0 0

             

            SetTransform(D3DTS_TEXTURE0, &M);

            用立方體貼圖實(shí)現(xiàn)環(huán)境貼圖

            • D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR標(biāo)志告訴系統(tǒng)自動(dòng)產(chǎn)生紋理坐標(biāo),并用作立方體貼圖的反射向量。

            SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);

            • 告訴光柵化器使用三元素的紋理坐標(biāo)。

            SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT3);

            實(shí)現(xiàn)投影紋理

            • 使用D3DTSS_TCI_CAMERASPACEPOSITION標(biāo)志告訴系統(tǒng)用頂點(diǎn)在攝像機(jī)空間中的位置作為紋理變換矩陣的輸入。

            SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);

            • 創(chuàng)建紋理變換矩陣并執(zhí)行變換,這超出了本文檔的范圍,計(jì)算機(jī)圖形工業(yè)中有許多文章討論這個(gè)主題。
            • 告訴光柵化器使用三元素投影紋理坐標(biāo)。

            ·                // 使用了兩個(gè)輸出紋理坐標(biāo)。

            ·                 

            SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTF_PROJECTED | D3DTTFF_COUNT3);


            紋理過(guò)濾


            Microsoft® Direct3D®在渲染圖元時(shí),要把三維圖元映射到二維屏幕上。如果圖元貼有紋理,那么Direct3D必須用該紋理給圖元在二維屏幕上對(duì)應(yīng)的每個(gè)像素產(chǎn)生一個(gè)顏色。對(duì)于每個(gè)像素,Direct3D必須從紋理獲得一個(gè)顏色值,這個(gè)過(guò)程被稱為紋理過(guò)濾。

            在執(zhí)行紋理過(guò)濾操作時(shí),正在使用的紋理一般會(huì)被放大或縮小,換句話說(shuō),就是紋理被貼到比它大或比它小的圖元上。紋理放大會(huì)使多個(gè)像素映射到一個(gè)texel,得到的圖像可能會(huì)有馬賽克。紋理縮小會(huì)使一個(gè)像素映射到多個(gè)texel,得到的圖像可能會(huì)模糊不清或有鋸齒。要解決這些問題,必須在計(jì)算像素的顏色時(shí)對(duì)texel的顏色進(jìn)行一些混合操作。

            Direct3D把復(fù)雜的紋理過(guò)濾過(guò)程簡(jiǎn)化了,它給應(yīng)用程序提供了三種類型的紋理過(guò)濾——線性過(guò)濾、各向異性過(guò)濾和mipmap過(guò)濾。如果應(yīng)用程序不選擇以上三種紋理過(guò)濾,那么Direct3D使用一種被稱為最近點(diǎn)取樣的技術(shù)。

            每種類型的紋理過(guò)濾都各有優(yōu)缺點(diǎn)。例如,線性紋理過(guò)濾可能會(huì)在最終圖像中產(chǎn)生鋸齒邊緣或馬賽克,但它是三種紋理過(guò)濾方法中計(jì)算量最小的。用mipmap紋理過(guò)濾通常可以得到最好的效果,尤其是和各向異性過(guò)濾一起使用的時(shí)候,但是在Direct3D支持的紋理過(guò)濾技術(shù)中,它對(duì)內(nèi)存的需求也最大。

            使用紋理接口指針的應(yīng)用程序應(yīng)該調(diào)用IDirect3DDevice9::SetSamplerState方法設(shè)置當(dāng)前的紋理過(guò)濾方法。第一個(gè)參數(shù)為從0到7的整數(shù),表示要設(shè)置紋理過(guò)濾方法的紋理層的索引值。第二個(gè)參數(shù)為D3DSAMP_MAGFILTERD3DSAMP_MINFILTERD3DSAMP_MIPFILTER,分別表示放大、縮小和mipmap過(guò)濾。第三個(gè)參數(shù)為D3DTEXTUREFILTERTYPE枚舉類型值,為要設(shè)置的紋理過(guò)濾方法。

            本節(jié)介紹了Direct3D支持的紋理過(guò)濾方法,并被劃為以下主題。

            注意 雖然D3DRENDERSTATETYPE枚舉類型中定義的紋理過(guò)濾渲染狀態(tài)已經(jīng)被紋理層狀態(tài)取代,但是如果應(yīng)用程序試圖使用這些渲染狀態(tài)的話,IDirect3DDevice9與IDirect3DDevice2不同的是,IDirect3DDevice9::SetRenderState方法不會(huì)失敗。相反,系統(tǒng)會(huì)把這些渲染狀態(tài)映射到多重紋理的第一層,也就是索引值為0的那層。應(yīng)用程序不應(yīng)該把老的渲染狀態(tài)和它們對(duì)應(yīng)的紋理層狀態(tài)混在一起,這樣可能會(huì)產(chǎn)生無(wú)法預(yù)知的結(jié)果。


            最近點(diǎn)取樣


            應(yīng)用程序不一定要使用紋理過(guò)濾。應(yīng)用程序可以讓Microsoft® Direct3D®先計(jì)算紋理地址(通常都不是整數(shù)),然后使用離該值最近的整數(shù)地址處的texel的顏色,這個(gè)過(guò)程被稱為最近點(diǎn)取樣。如果紋理的大小與圖元在屏幕上的大小相近的話,那么這將是快速且有效的紋理處理方法,但如果不是這樣的話,得到的圖像可能會(huì)有馬賽克、鋸齒或模糊不清。

            C++應(yīng)用程序可以調(diào)用IDirect3DDevice9::SetSamplerState方法選擇最近點(diǎn)取樣,只需把第三個(gè)參數(shù)設(shè)置為D3DTEXF_POINT即可。

            應(yīng)用程序在使用最近點(diǎn)取樣時(shí)應(yīng)該小心,因?yàn)楫?dāng)在兩個(gè)texel間的邊界處進(jìn)行紋理取樣時(shí),這種方法有時(shí)會(huì)產(chǎn)生圖形殘留物。當(dāng)使用最近點(diǎn)取樣時(shí),系統(tǒng)要么取樣一個(gè)texel,要么取樣另一個(gè),這樣當(dāng)紋理地址從一個(gè)texel移向下一個(gè)texel時(shí),取樣得到的texel會(huì)突然改變。這種效果會(huì)導(dǎo)致在最終顯示的紋理中出現(xiàn)不希望的圖形殘留物。當(dāng)使用線性過(guò)濾時(shí),取樣得到的texel是根據(jù)所有鄰近的texel計(jì)算得到的,當(dāng)紋理地址在鄰近的texel間移動(dòng)時(shí),線性過(guò)濾會(huì)根據(jù)當(dāng)前紋理地址與鄰近texel間的位置關(guān)系,把相鄰texel混合。

            當(dāng)把非常小的紋理貼到非常大的多邊形表面時(shí)可以看到這種效果:這個(gè)操作通常被稱為紋理放大magnification)。例如,當(dāng)使用的紋理看起來(lái)像西洋跳棋的棋盤時(shí),最近點(diǎn)取樣會(huì)得到一個(gè)非常大的棋盤,格子之間有清晰的邊緣,相比之下,線性紋理過(guò)濾得到的圖像中棋盤的顏色會(huì)沿著多邊形逐漸改變。

            大多數(shù)情況下,為了得到最好的效果,應(yīng)用程序應(yīng)該盡量避免使用最近點(diǎn)取樣。當(dāng)今的大多數(shù)硬件都為線性過(guò)濾做了優(yōu)化,所以應(yīng)用程序不必?fù)?dān)心因此導(dǎo)致的性能下降。如果應(yīng)用程序想要的效果一定要用最近點(diǎn)取樣——比如用紋理顯示可讀的文本——那么應(yīng)用程序應(yīng)該極度小心,避免在texel邊界取樣,因?yàn)槟菢訒?huì)導(dǎo)致不想得到的效果。下圖顯示了這些圖形殘留物可能的樣子。

             

            注意這組圖片中右上角的兩個(gè)方塊與其余的不同,可以在它們的對(duì)角線上看到明顯的偏移。要避免此類圖形殘留物,開發(fā)人員必須熟悉Direct3D在最近點(diǎn)取樣時(shí)使用的紋理取樣規(guī)則。Direct3D把閉區(qū)間[0.0, 1.0]范圍內(nèi)的浮點(diǎn)紋理坐標(biāo)映射到texel空間中的整數(shù)地址[–0.5, n – 0.5]范圍內(nèi),這里n為給定紋理的大小。得到的紋理地址被舍入到最近的整數(shù),這種映射方法在texel邊界會(huì)產(chǎn)生取樣誤差。

            舉個(gè)簡(jiǎn)單的例子,設(shè)想應(yīng)用程序用D3DTADDRESS_WRAP紋理尋址模式渲染多邊形。根據(jù)Direct3D使用的映射方法,對(duì)于寬度為4個(gè)texel的紋理,紋理在u方向上的映射如下。

             

            注意這張圖中的紋理坐標(biāo)0.0和1.0,正好在texel之間的邊界。根據(jù)Direct3D的映射方法,紋理坐標(biāo)的范圍為[–0.5, 4 0.5],這里4為紋理的寬度。在這個(gè)例子中,紋理坐標(biāo)為1.0處取樣得到的texel是texel 0。但是,如果紋理坐標(biāo)只比1.0稍微小一點(diǎn),那么取樣得到的就會(huì)是texel n而不是texel 0。

            這就意味著用正好等于0.0和1.0的紋理坐標(biāo)去放大較小的紋理,并把它貼到整齊排列在屏幕上的三角形時(shí),如果過(guò)濾方法為最近點(diǎn)取樣方法,那么得到的像素可能會(huì)在texel之間的邊界進(jìn)行取樣。在紋理坐標(biāo)計(jì)算過(guò)程中的任何誤差,無(wú)論多小,都可能在渲染得到的圖像上與texel邊界對(duì)應(yīng)的部分出現(xiàn)圖形殘留物。

            要完全精確地執(zhí)行從浮點(diǎn)紋理坐標(biāo)到整數(shù)texel地址的映射是很難的,也很耗時(shí),并且通常是不必要的。大多數(shù)硬件實(shí)現(xiàn)在給三角形內(nèi)的每個(gè)像素計(jì)算紋理坐標(biāo)時(shí)使用迭代法。迭代法趨向于隱藏誤差,因?yàn)檎`差在迭代過(guò)程中被均勻地累積起來(lái)。

            Direct3D參考光柵化器在給每個(gè)像素計(jì)算紋理地址時(shí)使用直接賦值法。直接賦值法與迭代法的不同之處在于這種方法產(chǎn)生的誤差分布更隨機(jī)。因?yàn)閰⒖脊鈻呕鞑贿M(jìn)行完全精確的計(jì)算,所以這種方法會(huì)導(dǎo)致在邊界處的取樣誤差更容易被察覺。

            最好的方法是只在必要的時(shí)候使用最近點(diǎn)取樣。如果應(yīng)用程序必須使用這種方法,那么最好把紋理坐標(biāo)稍微偏移一些使之離開邊界位置,這樣就可以避免殘留物的產(chǎn)生。


            線性紋理過(guò)濾


            Microsoft® Direct3D®使用一種被稱為雙線性過(guò)濾的線性紋理過(guò)濾方法。和最近點(diǎn)取樣一樣,雙線性過(guò)濾首先計(jì)算一個(gè)texel地址,這通常都不會(huì)是整數(shù),然后找到離該地址最近的整數(shù)地址。另外,Direct3D渲染模塊還會(huì)根據(jù)最近取樣點(diǎn)上、下、左、右的texel計(jì)算它們的加權(quán)平均值。

            可以調(diào)用IDirect3DDevice9::SetSamplerState方法選擇雙線性過(guò)濾,只需把第三個(gè)參數(shù)設(shè)為D3DTEXF_LINEAR即可。


            各向異性紋理過(guò)濾


            因?yàn)槿S物體表面與屏幕間的夾角而造成的紋理扭曲被稱為各向異性。當(dāng)把一個(gè)各向異性的圖元所對(duì)應(yīng)的像素映射到texel時(shí),像素的形狀會(huì)被扭曲。Microsoft® Direct3D®根據(jù)像素被反向映射到紋理空間中的伸長(zhǎng)率——也就是長(zhǎng)度除以寬度——計(jì)量屏幕上像素的各向異性屬性。

            為了提高渲染質(zhì)量,應(yīng)用程序可以把各向異性過(guò)濾與線性紋理過(guò)濾或mipmap紋理過(guò)濾結(jié)合在一起使用。應(yīng)用程序可以調(diào)用IDirect3DDevice9::SetSamplerState方法啟用各向異性紋理過(guò)濾,只需把第三個(gè)參數(shù)設(shè)為D3DTEXF_ANISOTROPIC即可。

            應(yīng)用程序必須同時(shí)把degree of anisotropy設(shè)為大于一的值。可以調(diào)用IDirect3DDevice9::SetSamplerState方法設(shè)置這個(gè)值。第一個(gè)參數(shù)為0到7的紋理層索引值,把第二個(gè)參數(shù)設(shè)為D3DSAMP_MAXANISOTROPY,第三個(gè)參數(shù)為要設(shè)置的degree of anisotropy(譯注:原文為degree of isotropy)的值。

            應(yīng)用程序只需把degree of anisotropy(譯注:原文為degree of isotropy)設(shè)為一即可禁用各向異性過(guò)濾。要確定degree of anisotropy的允許范圍,可以檢查D3DCAPS9結(jié)構(gòu)的MaxAnisotropy成員。


            Mipmap進(jìn)行紋理過(guò)濾


            Mipmap是一系列紋理,每一張紋理都表示同一幅圖像,但是分辨率逐漸變低。Mipmap中的每張圖像,或每一級(jí),都比前一級(jí)小一半。Mipmap不必是正方形的。

            較高分辨率的mipmap圖像用于離用戶近的物體,而較低分辨率的圖像則用于遠(yuǎn)處的物體。使用Mipmap在消耗更多內(nèi)存的情況下,提高了渲染得到的紋理的質(zhì)量。

            Microsoft® Direct3D®用一連串從屬表面表示mipmap。分辨率最高的紋理在鏈的最前端,下一級(jí)mipmap是它的從屬表面。依次,每一級(jí)mipmap的從屬表面是它在mipmap中的下一級(jí),一直到mipmap中分辨率最低的那級(jí)。

            下圖顯示了這樣的例子。該紋理表示在一個(gè)三維第一人稱游戲中的一個(gè)容器上的標(biāo)記。創(chuàng)建mipmap時(shí),最高分辨率的紋理是mipmap中的第一個(gè),mipmap中每個(gè)隨后的紋理的寬度和高度都是原來(lái)的一半,在這個(gè)例子中,最高分辨率的mipmap為256x256。下一級(jí)紋理為128x128,最后一級(jí)紋理為64x64。

            這個(gè)標(biāo)記有一個(gè)最遠(yuǎn)可見距離。如果用戶離標(biāo)記很遠(yuǎn),那么游戲就用mipmap鏈中最小的紋理顯示,在這個(gè)例子中就是64x64的紋理。

             

            隨著用戶移動(dòng)視點(diǎn)并離標(biāo)記越來(lái)越近,游戲會(huì)逐漸使用mipmap鏈中更高分辨率的紋理。下圖中紋理的分辨率為128x128。

             

            當(dāng)視點(diǎn)離標(biāo)記的距離為所允許的最近距離時(shí),游戲就使用最高分辨率的紋理。

             

            對(duì)于紋理而言,這是一種更有效的模擬透視的方法,與在不同的分辨率下用單張紋理進(jìn)行渲染相比,在不同分辨率下使用多張紋理會(huì)更快。

            Direct3D可以確定mipmap鏈中哪一級(jí)紋理的分辨率與當(dāng)前需要的最為接近,并把像素映射到那一級(jí)紋理的texel空間。如果最終圖像需要的分辨率位于mipmap鏈中兩級(jí)紋理的分辨率之間,Direct3D會(huì)取得這兩級(jí)mipmap中的texel并把它們的顏色值混合在一起。

            要使用mipmap,應(yīng)用程序必須創(chuàng)建一個(gè)mipmap鏈。應(yīng)用程序只需把mipmap鏈設(shè)為當(dāng)前紋理可以使用mipmap。更多信息,請(qǐng)參閱紋理混合

            下一步,應(yīng)用程序必須設(shè)置Direct3D用于取樣texel的紋理過(guò)濾方法。Mipmap過(guò)濾最快的方法就是讓Direct3D選擇最近的texel,D3DTEXF_POINT枚舉類型值就是用來(lái)選擇這種方法的。如果應(yīng)用程序使用D3DTEXF_LINEAR枚舉類型值,那么Direct3D可以產(chǎn)生更好的過(guò)濾效果,這會(huì)使Direct3D選擇最近的那級(jí)mipmap,然后根據(jù)當(dāng)前像素在那一級(jí)mipmap中所映射的texel及其附近的texel計(jì)算加權(quán)平均值。

            Mipmap紋理用于減少渲染三維場(chǎng)景所需的時(shí)間,同時(shí)提高了場(chǎng)景的真實(shí)感。但是,mipmap通常需要大量的內(nèi)存。

            創(chuàng)建mipmap

            以下示例代碼顯示了應(yīng)用程序如何調(diào)用IDirect3DDevice9::CreateTexture方法創(chuàng)建一條五級(jí)mipmap鏈:256x256, 128x128, 64x64, 32x32, 及16x16。

            // 本例假設(shè)變量d3dDevice為指向IDirect3DDevice9接口的有效指針。
             
            IDirect3DTexture9 * pMipMap;
            d3dDevice->CreateTexture(256, 256, 5, 0, D3DFMT_R8G8B8, D3DPOOL_MANAGED, &pMipMap);

            IDirect3DDevice9::CreateTexture的前兩個(gè)參數(shù)為最高一級(jí)的紋理的寬和高。第三個(gè)參數(shù)為mipmap紋理的級(jí)數(shù),如果應(yīng)用程序把它設(shè)為零,那么Direct3D會(huì)創(chuàng)建一系列表面,每個(gè)都是前一個(gè)的一半,直到大小為1x1為止。第四個(gè)參數(shù)指定該資源的用途,本例中,零表示不為該資源指定特殊的用途。第五個(gè)參數(shù)指定紋理的表面格式,該參數(shù)為D3DFORMAT枚舉類型值。第六個(gè)參數(shù)為D3DPOOL枚舉類型值,表示在把創(chuàng)建的資源放在哪種類型的內(nèi)存中,除非應(yīng)用程序使用動(dòng)態(tài)紋理,否則建議使用D3DPOOL_MANAGED。最后一個(gè)參數(shù)為指向IDirect3DTexture9接口指針的地址。

            注意    Mipmap鏈中的每個(gè)表面的大小都是前一個(gè)表面的一半。如果最高一級(jí)mipmap的大小為256x128,那么第二級(jí)的mipmap就是128x64,第三級(jí)為64x32,依次類推,直到1x1(譯注:最后幾級(jí)分別為:4x2,2x11x1)。應(yīng)用程序在Levels中要求的mipmap級(jí)不能使鏈中的任何mipmap的寬和高小于1。舉個(gè)簡(jiǎn)單的例子,如果最高一級(jí)的mipmap表面為4x2,那么Levels的最大允許值就是三,第三層的大小為1x1。如果Levels的值大于3,那么會(huì)導(dǎo)致第二級(jí)mipmap的高度值出現(xiàn)小數(shù),而這是不允許的。(譯注:原文表述不夠準(zhǔn)確,應(yīng)該是log2 (max (width, height) ) < 0

            選擇并顯示mipmap

            可以調(diào)用IDirect3DDevice9::SetTexture方法把mipmap紋理設(shè)置為當(dāng)前紋理中的第一個(gè)紋理,更多信息請(qǐng)參閱紋理混合

            應(yīng)用程序在選擇mipmap紋理后,必須用D3DTEXTUREFILTERTYPE枚舉類型值設(shè)置D3DSAMP_MIPFILTER取樣器狀態(tài)。之后Direct3D就可以自動(dòng)執(zhí)行mipmap紋理過(guò)濾。以下示例代碼顯了如何啟用mipmap紋理過(guò)濾。

            d3dDevice->SetTexture(0, pMipMap);
            d3dDevice->SetTextureStageState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

            應(yīng)用程序也可以手工遍歷mipmap鏈,只需調(diào)用IDirect3DTexture9::GetSurfaceLevel方法,并指定要取得的mipmap級(jí)即可。以下示例代碼從mipmap鏈的最高一級(jí)遍歷到最低一級(jí)。

            IDirect3DSurface9 * pSurfaceLevel;
            for (int iLevel = 0; iLevel < pMipMap->GetLevelCount(); iLevel++)
            {
                pMipMap->GetSurfaceLevel(iLevel, &pSurfaceLevel);
                //Process this level.
                pSurfaceLevel->Release();
            }

            為了把位圖數(shù)據(jù)載入mipmap鏈中的每個(gè)表面,應(yīng)用程序需要手工遍歷mipmap鏈,一般來(lái)說(shuō)這是遍歷mipmap鏈的唯一原因。應(yīng)用程序可以通過(guò)調(diào)用IDirect3DBaseTexture9::GetLevelCount取得mipmap的級(jí)數(shù)。


            紋理資源


            紋理資源在IDirect3DTexture9接口中實(shí)現(xiàn)。要得到一個(gè)指向紋理接口的指針,應(yīng)該調(diào)用IDirect3DDevice9::CreateTexture方法或以下Direct3D擴(kuò)展(D3DX)函數(shù)。

            • D3DXCreateTexture
            • D3DXCreateTextureFromFile
            • D3DXCreateTextureFromFileEx
            • D3DXCreateTextureFromFileInMemory
            • D3DXCreateTextureFromFileInMemoryEx
            • D3DXCreateTextureFromResource
            • D3DXCreateTextureFromResourceEx

            以下示例代碼調(diào)用D3DXCreateTextureFromFile從文件Tiger.bmp中載入一張紋理。

            // 以下示例代碼假設(shè)D3dDevice為指向IDirect3DDevice9接口的有效指針。

             

            LPDIRECT3DTEXTURE9 pTexture;

             

            D3DXCreateTextureFromFile( d3dDevice, "tiger.bmp", &pTexture);

            D3DXCreateTextureFromFile的第一個(gè)參數(shù)為指向IDirect3DDevice9接口的指針。第二個(gè)參數(shù)為文件名,告訴Direct3D從哪個(gè)文件載入紋理。第三個(gè)參數(shù)為指向IDirect3DTexture9接口的指針的地址,表示創(chuàng)建得到的紋理對(duì)象。

            用紋理資源進(jìn)行渲染

            Direct3D通過(guò)紋理層的概念支持多重紋理混合,每個(gè)紋理層包含一張紋理以及可以在這張紋理上執(zhí)行的操作。紋理層中的紋理組成了一個(gè)當(dāng)前紋理的集合。更多信息請(qǐng)參閱紋理混合。每張紋理的狀態(tài)被封裝在對(duì)應(yīng)的紋理層中。

            C++應(yīng)用程序必須調(diào)用IDirect3DDevice9::SetTextureStageState方法設(shè)置每張紋理的狀態(tài)。第一個(gè)參數(shù)為從0到7的紋理層索引值,第二個(gè)參數(shù)為D3DTEXTURESTAGESTATETYPE枚舉類型值,最后一個(gè)參數(shù)為要設(shè)置的紋理層狀態(tài)值。

            在使用紋理接口指針進(jìn)行渲染時(shí),應(yīng)用程序最多可以把八張紋理混合。應(yīng)用程序通過(guò)調(diào)用IDirect3DDevice9::SetTexture方法設(shè)置當(dāng)前紋理。Direct3D會(huì)先把所有當(dāng)前紋理混合,然后再貼到正在渲染的圖元的表面。

            注意 因?yàn)?strong>IDirect3DDevice9::SetTexture方法會(huì)增加正在設(shè)置的紋理表面的參考計(jì)數(shù)(reference count),所以當(dāng)不再需要該紋理時(shí),應(yīng)用程序應(yīng)該把相應(yīng)紋理層的紋理設(shè)置為NULL。如果應(yīng)用程序不這樣做,那么表面就不會(huì)被釋放,并造成內(nèi)存泄漏。

            應(yīng)用程序可以調(diào)用IDirect3DDevice9::SetRenderState方法設(shè)置當(dāng)前紋理的紋理環(huán)繞狀態(tài),只需把第一個(gè)參數(shù)設(shè)為從D3DRS_WRAP0到D3DRS_WRAP7的值,并把第二個(gè)參數(shù)設(shè)為D3DWRAPCOORD_0,D3DWRAPCOORD1,D3DWRAPCOORD2,及D3DWRAPCOORD3標(biāo)志的組合,這樣就可以啟用在u, v, 或w方向上的紋理環(huán)繞。

            應(yīng)用程序還可以設(shè)置紋理透視和紋理過(guò)濾狀態(tài)。請(qǐng)參閱紋理過(guò)濾


            紋理環(huán)繞


            簡(jiǎn)而言之,紋理環(huán)繞用來(lái)改變Microsoft® Direct3D®用每個(gè)頂點(diǎn)的紋理坐標(biāo)對(duì)貼有紋理的多邊形進(jìn)行光柵化的基本方法。在光柵化一個(gè)多邊形時(shí),為了確定多邊形所覆蓋的每個(gè)像素所需使用的 texel,系統(tǒng)要在每個(gè)多邊形頂點(diǎn)的紋理坐標(biāo)之間進(jìn)行插值。一般來(lái)說(shuō),系統(tǒng)把紋理當(dāng)做二維平面,為了在紋理中的點(diǎn)A和點(diǎn)B之間插值得到新的texel,系統(tǒng)會(huì)取兩點(diǎn)間的最短路徑。如果點(diǎn)A表示的u, v坐標(biāo)為(0.8, 0.1),點(diǎn)B表示的u, v坐標(biāo)為(0.1,0.1),那么插值的路徑會(huì)如下圖所示。

             

            注意這張圖中點(diǎn)A和點(diǎn)B間的最短路徑大致穿越紋理的中央。啟用u方向或v方向上的紋理坐標(biāo)環(huán)繞會(huì)相應(yīng)改變Direct3D在u方向或v方向上對(duì)紋理坐標(biāo)間最短路徑的理解。紋理環(huán)繞的定義使光柵化器在獲取紋理坐標(biāo)間的最短路徑時(shí),假設(shè)紋理坐標(biāo)0.0和1.0是等價(jià)的。最后這一點(diǎn)是技巧所在:可以這樣想象,在某一方向上啟用紋理環(huán)繞會(huì)使系統(tǒng)把紋理當(dāng)成在圓柱體表面環(huán)繞的紋理進(jìn)行處理。例如,考慮下圖。

             

            這幅圖顯示了在u方向上的環(huán)繞是怎樣影響系統(tǒng)對(duì)紋理坐標(biāo)間的插值的。在一張普通的,或沒有環(huán)繞的紋理上使用與前例中相同的點(diǎn),我們會(huì)發(fā)現(xiàn)點(diǎn)A和點(diǎn)B間的最短路徑不再穿越紋理的中央,而是穿越紋理的邊框,也就是紋理坐標(biāo)0.0和1.0重合的地方。在v方向上的環(huán)繞與此類似,唯一的不同在于圓柱體是平躺的。同時(shí)在u方向和v方向上進(jìn)行環(huán)繞比較復(fù)雜,在這種情況下,可以把紋理想象成是一個(gè)立體圓環(huán)。

            紋理環(huán)繞最通常的應(yīng)用是在使用環(huán)境貼圖時(shí)。通常,使用環(huán)境貼圖的物體會(huì)顯得比較反光,并反射出物體周圍的場(chǎng)景。為了便于討論,我們想象一個(gè)房間,房間有四面墻,每面墻上寫著字母R, G, B, Y,分別對(duì)應(yīng)顏色紅,綠,藍(lán),黃。這樣一個(gè)簡(jiǎn)單的房間使用的環(huán)境貼圖可能如下圖所示。

             

            設(shè)想房屋的屋頂由一根完全反光的柱子支撐,柱子有四面。要把環(huán)境貼圖貼到柱子上很簡(jiǎn)單,但是要讓柱子看起來(lái)像是反射墻上的字母和顏色就沒有那么容易了。下圖顯示了用線框模式繪制的柱子,并在頂部的頂點(diǎn)處列出了相應(yīng)的紋理坐標(biāo)。環(huán)繞將發(fā)生在紋理的接縫處,在下圖中用虛線表示。

             

            如果在u方向上啟用紋理環(huán)繞,那么柱子會(huì)正確地顯示出環(huán)境貼圖上的顏色和字母,并且在紋理前面的接縫處,光柵化器會(huì)假設(shè)u坐標(biāo)0.0和1.0是等價(jià)的并正確地選擇紋理坐標(biāo)間的最短路徑。貼上紋理后的柱子如下圖所示。

             

            如果沒有啟用紋理環(huán)繞,那么光柵化器就不會(huì)產(chǎn)生可信的反射圖像。相反,柱子前面的部分會(huì)包含經(jīng)過(guò)擠壓的位于u坐標(biāo)0.175到0.875之間的texel,因?yàn)檫@些texel穿越紋理的中央。這樣環(huán)繞效果被破壞了。

            使用紋理環(huán)繞

            要啟用紋理環(huán)繞,應(yīng)該調(diào)用IDirect3DDevice9::SetRenderState方法,如以下示例代碼所示。

            d3dDevice->SetRenderState(D3DRS_WRAP0, D3DWRAPCOORD_0);

            IDirect3DDevice9::SetRenderState的第一個(gè)參數(shù)是要設(shè)置的渲染狀態(tài),應(yīng)該設(shè)為從D3DRS_WRAP0到D3DRS_WRAP7的枚舉類型值,表示要設(shè)置哪一個(gè)紋理層的環(huán)繞狀態(tài)。把第二個(gè)參數(shù)設(shè)為從D3DWRAPCOORD_0到D3DWRAPCOORD_3的標(biāo)志可以在對(duì)應(yīng)的方向上啟用紋理環(huán)繞,也可以組合使用這些標(biāo)志在多個(gè)方向上啟用紋理環(huán)繞。如果應(yīng)用程序忽略其中某個(gè)標(biāo)志,那么在相應(yīng)的方向上的紋理環(huán)繞就被禁用,要禁用某組紋理坐標(biāo)在所有方向上的紋理環(huán)繞,只需把第二個(gè)參數(shù)設(shè)為0。

            不要把紋理環(huán)繞與名字相近的紋理尋址模式相混淆。紋理環(huán)繞在對(duì)紋理進(jìn)行尋址之前進(jìn)行。一定要保證用于紋理環(huán)繞的數(shù)據(jù)不包含[0.0, 1.0]范圍外的紋理坐標(biāo),因?yàn)槟菢訒?huì)導(dǎo)致不希望的結(jié)果。有關(guān)紋理尋址的更多信息,請(qǐng)參閱紋理尋址模式

            位移貼圖的環(huán)繞

            位移貼圖由tessellation引擎解釋,因?yàn)闊o(wú)法為tessellation引擎指定環(huán)繞模式,所以不能對(duì)位移貼圖進(jìn)行紋理環(huán)繞。應(yīng)用程序可以用一組頂點(diǎn),強(qiáng)制進(jìn)行在任何方向上的環(huán)繞。應(yīng)用程序也可以指定只進(jìn)行簡(jiǎn)單的線性插值。


            紋理混合


            Microsoft® Direct3D®最多可以在一趟渲染過(guò)程中把八張紋理混合并貼到圖元上。使用多重紋理可以極大地提高Direct3D應(yīng)用程序的執(zhí)行速度。應(yīng)用程序可以用多重紋理混合在一趟渲染過(guò)程中產(chǎn)生紋理、影子、鏡面反射光、漫反射光,以及其它特效。

            要使用紋理混合,應(yīng)用程序必須先檢查硬件是否支持紋理混合。這個(gè)信息包含在D3DCAPAS9結(jié)構(gòu)的TextureCaps成員中。有關(guān)如何查詢硬件的紋理混合能力的細(xì)節(jié),請(qǐng)參閱IDirect3DDevice9::GetDeviceCaps

            紋理層和紋理混合級(jí)聯(lián)

            通過(guò)使用紋理層,Direct3D支持在一趟渲染過(guò)程中完成多重紋理混合。一個(gè)紋理層有兩個(gè)輸入,并對(duì)它們執(zhí)行一個(gè)混合操作,然后把結(jié)果用于進(jìn)一步的處理或用于光柵化。可以把紋理層想象為如下圖所示。

             

            如上圖所示,紋理層用一個(gè)指定的操作符把兩個(gè)輸入混合。常用的操作符包括對(duì)輸入?yún)?shù)的顏色或阿爾法的簡(jiǎn)單調(diào)制或相加,但實(shí)際上Direct3D支持幾十種混合操作。紋理層的輸入可以是與該層關(guān)聯(lián)的紋理,迭代后的顏色或阿爾法(在進(jìn)行高洛德著色的過(guò)程中迭代得到),指定的顏色或阿爾法,或前一個(gè)紋理層的結(jié)果。更多信息,請(qǐng)參閱紋理混合操作和輸入

            注意 Direct3D區(qū)分顏色混合和阿爾法混合。應(yīng)用程序分別設(shè)置要對(duì)顏色和阿爾法執(zhí)行的混合操作及相應(yīng)的輸入,并且這些設(shè)置互不影響。

            多重混合層的參數(shù)和操作的組合定義了一種簡(jiǎn)單的基于流程的混合語(yǔ)言。每一層的結(jié)果流入下一層,依次類推。這個(gè)概念,也就是混合的結(jié)果在層與層之間流動(dòng),并最終被用來(lái)對(duì)多邊形進(jìn)行光柵化操作,通常被稱為紋理混合級(jí)聯(lián)。下圖顯示了各獨(dú)立的紋理層如何組成紋理混合級(jí)聯(lián)。

             

            設(shè)備中的每個(gè)紋理層都有一個(gè)從零開始的索引值。雖然應(yīng)用程序應(yīng)該總是檢查設(shè)備的能力以確定當(dāng)前設(shè)備支持幾層紋理,但是Direct3D最多允許有八個(gè)混合層。第一層的索引值為0,第二層的索引值為1,依次類推,直到索引值7。系統(tǒng)根據(jù)索引值的增長(zhǎng)混合各紋理層。

            最好只使用需要的那些混合層,默認(rèn)情況下沒有用到的混合層被禁用。因此,如果應(yīng)用程序只使用前兩個(gè)混合層,那么只需設(shè)置層0和層1的操作符和參數(shù)。系統(tǒng)會(huì)對(duì)這兩層執(zhí)行混合操作,并忽略其余被禁用的層。

            有關(guān)性能的注意事項(xiàng)    如果應(yīng)用程序在不同的情況下使用不同數(shù)量的紋理層——比如對(duì)一些物體使用四層紋理,而對(duì)其它物體只使用兩層紋理——那么應(yīng)用程序無(wú)需顯式地禁止所有以前使用過(guò)的層。一種選擇是對(duì)未曾用到的紋理層的第一層,禁用其顏色操作符,這樣該紋理層及其后的紋理層將不會(huì)被用到。另一種選擇是設(shè)置第一層紋理(層0)的顏色操作符,禁用所有紋理貼圖。第三種選擇是當(dāng)紋理層的D3DTSS_COLORARG1等于D3DTA_TEXTURE時(shí),只要該層的紋理指針為空,該層及其后的紋理層都不會(huì)被處理。

            以下主題包含了更多信息。


            紋理混合操作及參數(shù)


            應(yīng)用程序把混合層與當(dāng)前紋理集合中的每張紋理相聯(lián)系。Microsoft® Direct3D®按照順序?qū)γ總€(gè)混合層求值,從集合中的第一張紋理開始,至第八張結(jié)束。

            Direct3D把當(dāng)前紋理集合中每張紋理的信息應(yīng)用于與之相聯(lián)系的混合層。通過(guò)調(diào)用IDirect3DDevice9::SetTextureStageState,應(yīng)用程序可以控制使用紋理層中的哪些信息。應(yīng)用程序可以分別設(shè)置對(duì)顏色和阿爾法通道的操作,每個(gè)操作有兩個(gè)參數(shù)。用D3DTSS_COLOROP紋理層狀態(tài)指定要對(duì)顏色通道執(zhí)行的操作,用D3DTSS_ALPHAOP紋理層狀態(tài)指定要對(duì)阿爾法通道執(zhí)行的操作,這兩個(gè)紋理層狀態(tài)都是D3DTEXTUREOP枚舉類型值。

            紋理混合的參數(shù)使用D3DTEXTURESTAGESTATETYPE枚舉類型的D3DTSS_COLORARG1, D3DTSS_COLORARG2, D3DTSS_ALPHAARG1和D3DTSS_ALPHAARG2成員表示。對(duì)應(yīng)的參數(shù)值由D3DTA指定。

            注意 通過(guò)把某一層的顏色操作設(shè)置為D3DTOP_DISABLE,應(yīng)用程序可以禁用該紋理層及紋理混合級(jí)聯(lián)中所有的后續(xù)層。禁用顏色操作會(huì)同時(shí)禁用阿爾法操作。當(dāng)顏色操作被啟用時(shí),阿爾法操作無(wú)法被禁用。當(dāng)顏色混合被啟用時(shí),把阿爾法操作設(shè)置為D3DTOP_DISABLE會(huì)導(dǎo)致不確定的結(jié)果。

            要測(cè)定一個(gè)設(shè)備支持的紋理混合操作,請(qǐng)查詢D3DCAPS9結(jié)構(gòu)的TextureCaps成員。


            設(shè)置當(dāng)前紋理


            Microsoft® Direct3D®維護(hù)著一個(gè)當(dāng)前紋理列表,最多可以有八張。Direct3D會(huì)把這些紋理混合到要渲染的圖元上。只有作為紋理接口指針創(chuàng)建的紋理可以被用于當(dāng)前紋理集合。

            應(yīng)用程序可以調(diào)用IDirect3DDevice9::SetTexture方法把紋理設(shè)置到當(dāng)前紋理集合中。第一個(gè)參數(shù)必須是閉區(qū)間0到7之間的數(shù)字,第二個(gè)參數(shù)是紋理接口指針。

            以下C++示例代碼顯示了如何把一張紋理加入到當(dāng)前紋理集合中。

            // 本示例代碼假設(shè)變量lpd3dDev為指向IDirect3DDevice9接口的有效指針,

            // 且pTexture為指向IDirect3DBaseTexture9接口的有效指針。

             

            // 設(shè)置第三層紋理

            d3dDevice->SetTexture(2, pTexture);

            注意 軟件設(shè)備不支持同時(shí)把一張紋理指定到一個(gè)以上的紋理層。


            創(chuàng)建混合層


            一個(gè)混合層是一個(gè)紋理操作及相應(yīng)參數(shù)的集合,它定義了怎樣混合紋理。C++應(yīng)用程序在創(chuàng)建混合層時(shí)調(diào)用IDirect3DDevice9::SetTextureStageState方法。第一次調(diào)用指定要執(zhí)行的操作,另外兩次調(diào)用指定參數(shù),Direct3D將用這兩個(gè)參數(shù)執(zhí)行指定的操作。以下示例代碼描述了如何創(chuàng)建一個(gè)混合層。

            // 本示例代碼假設(shè)lpD3DDev為指向IDirect3DDevice9接口的有效指針。

             

            // 設(shè)置要對(duì)第一個(gè)紋理進(jìn)行的操作

            d3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD);

             

            // 將參數(shù)1設(shè)置為紋理顏色

            d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);

             

            // 將參數(shù)2設(shè)置為迭代后的漫反射色。

            d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

            紋理中的texel數(shù)據(jù)包含顏色和阿爾法值。應(yīng)用程序可以在一個(gè)混合層中分別定義顏色和阿爾法操作。顏色操作和阿爾法操作分別有自己的參數(shù)。更多細(xì)節(jié),請(qǐng)參閱D3DTEXTURESTAGESTATETYPE

            雖然下面的宏并不是Microsoft® Direct3D®應(yīng)用程序編程接口(API)的一部分,但是應(yīng)用程序可以用它們簡(jiǎn)化創(chuàng)建紋理混合層所需的代碼。

            #define SetTextureColorStage( dev, i, arg1, op, arg2 )     \

               dev->SetTextureStageState( i, D3DTSS_COLOROP, op);      \

               dev->SetTextureStageState( i, D3DTSS_COLORARG1, arg1 ); \

               dev->SetTextureStageState( i, D3DTSS_COLORARG2, arg2 );

             

            #define SetTextureAlphaStage( dev, i, arg1, op, arg2 )     \

               dev->SetTextureStageState( i, D3DTSS_ALPHAOP, op);      \

               dev->SetTextureStageState( i, D3DTSS_ALPHAARG1, arg1 ); \

               dev->SetTextureStageState( i D3DTSS_ALPHAARG2, arg2 );

             


            阿爾法紋理混合


            Microsoft® Direct3D®在渲染一個(gè)圖元時(shí)會(huì)根據(jù)圖元的材質(zhì)、或圖元的頂點(diǎn)顏色,及光照信息為該圖元產(chǎn)生一個(gè)顏色。更多細(xì)節(jié),請(qǐng)參閱光照與材質(zhì)。如果應(yīng)用程序啟用紋理混合,那么Direct3D必須把經(jīng)過(guò)處理的多邊形上的像素顏色與已經(jīng)儲(chǔ)存在幀緩存中的像素顏色進(jìn)行混合。Direct3D使用以下公式計(jì)算圖元的(渲染得到的)圖像中每個(gè)像素的最終顏色。

            FinalColor = TexelColor × SourceBlendFactor + PixelColor × DestBlendFactor

            在這個(gè)公式中,FinalColor為輸出到目標(biāo)渲染表面的最終像素顏色。TexelColor為經(jīng)過(guò)紋理過(guò)濾的輸入顏色值,對(duì)應(yīng)當(dāng)前像素。有關(guān)Direct3D如何把texel映射到像素的細(xì)節(jié),請(qǐng)參閱紋理過(guò)濾SourceBlendFactor為一個(gè)經(jīng)過(guò)計(jì)算的值,Direct3D用它計(jì)算輸入顏色值在最終顏色中所占的百分比。PixelColor為當(dāng)前存儲(chǔ)在圖元的圖像中像素的當(dāng)前顏色。DestBlendFactor為當(dāng)前像素顏色在最終渲染得到的像素中所占的百分比。SourceBlendFactorDestBlendFactor的值在閉區(qū)間0.0到1.0范圍內(nèi)。

            正如我們從以上的公式中所看到的,如果SourceBlendFactorD3DBLEND_ONE且DestBlendFactor為D3DBLEND_ZERO,那么最終渲染得到的像素是不透明的。如果SourceBlendFactor為D3DBLEND_ZERO且DestBlendFactor為D3DBLEND_ONE,那么得到的像素是完全透明的。如果應(yīng)用程序把這些因子設(shè)置為為任何其它值,那么最終渲染得到的像素會(huì)具有一定的透明度。

            經(jīng)過(guò)紋理過(guò)濾后,每個(gè)像素的顏色值包含紅、綠和藍(lán)三種顏色值。默認(rèn)情況下,Direct3D使用D3DBLEND_SRCALPHA作為SourceBlendFactor,使用D3DBLEND_INVSRCALPHA作為DestBlendFactor。因此,通過(guò)設(shè)置紋理中的阿爾法值,應(yīng)用程序可以控制處理后的像素的透明度。

            C++應(yīng)用程序可以用D3DRS_SRCBLEND和D3DRS_DESTBLEND渲染狀態(tài)控制這些因子,只需調(diào)用IDirect3DDevice9::SetRenderState方法,把這兩個(gè)渲染狀態(tài)其中之一作為第一個(gè)參數(shù)傳入。第二個(gè)參數(shù)必須是D3DBLEND枚舉類型成員。


            多趟紋理混合


            通過(guò)在多趟渲染的過(guò)程中將多個(gè)紋理貼到一個(gè)圖元的表面,Microsoft® Direct3D®應(yīng)用程序可以實(shí)現(xiàn)許多特效,這通常被稱為多趟(multipass)紋理混合。多趟紋理混合的一個(gè)典型用途就是通過(guò)把幾個(gè)不同紋理上的顏色混合,模擬復(fù)雜的光照和著色模型的效果。這種應(yīng)用被稱為光照貼圖。更多信息,請(qǐng)參閱用紋理實(shí)現(xiàn)光照貼圖

            注意 一些設(shè)備可以在一趟渲染過(guò)程中將多張紋理貼到圖元表面。細(xì)節(jié)請(qǐng)參閱紋理混合

            如果用戶的硬件不支持多重紋理混合,應(yīng)用程序可以使用多趟紋理混合以達(dá)到同樣的視覺效果。但是,與使用多重紋理混合相比,應(yīng)用程序?qū)o(wú)法保持相同的幀速率。

            要進(jìn)行多趟紋理混合,C++應(yīng)用程序應(yīng)該執(zhí)行以下操作。

            1. 調(diào)用IDirect3DDevice9::SetTexture方法給紋理層0設(shè)置一張紋理。
            2. 調(diào)用IDirect3DDevice9::SetTextureStageState方法設(shè)置相應(yīng)的顏色和阿爾法混合操作及參數(shù)。默認(rèn)的設(shè)定就很適合用于多趟紋理混合。
            3. 渲染場(chǎng)景中相應(yīng)的物體。
            4. 將下一張紋理指定到紋理層0。
            5. 根據(jù)需要設(shè)置D3DRS_SRCBLENDD3DRS_DESTBLEND渲染狀態(tài)以調(diào)整源和目的混合因子。系統(tǒng)根據(jù)這些參數(shù)把新的紋理和已經(jīng)存在于渲染目標(biāo)表面中的像素進(jìn)行混合。
            6. 根據(jù)所需紋理的數(shù)量,重復(fù)步驟3,4,5。

            用紋理實(shí)現(xiàn)光照貼圖


            對(duì)于想要真實(shí)地渲染一個(gè)三維場(chǎng)景的應(yīng)用程序來(lái)說(shuō),必須要考慮光源會(huì)對(duì)渲染得到的場(chǎng)景產(chǎn)生的效果。雖然諸如平面著色和高洛德著色之類的技術(shù)在這方面也是有用的工具,但它們可能無(wú)法滿足應(yīng)用程序的要求。Microsoft® Direct3D®支持多趟和多重紋理混合。與僅使用著色技術(shù)相比,這些能力使應(yīng)用程序能夠渲染更具真實(shí)感的場(chǎng)景。通過(guò)使用一張或多張光照貼圖,應(yīng)用程序可以把光影的范圍映射到圖元上。

            光照貼圖是包含三維場(chǎng)景中光照信息的一張紋理或一組紋理。應(yīng)用程序可以把光照信息存放在光照貼圖的阿爾法值中,顏色值中,或以上兩者中。

            如果應(yīng)用程序用多趟紋理混合實(shí)現(xiàn)光照貼圖,應(yīng)用程序應(yīng)該在第一趟渲染時(shí)把光照貼圖貼到圖元上,在第二次渲染時(shí)使用基本紋理。鏡面反射光照貼圖是個(gè)例外,在這種情況下,要先渲染基本紋理,再添加光照貼圖。

            多重紋理混合使應(yīng)用程序能一次同時(shí)渲染光照貼圖和基本紋理。如果用戶的硬件支持多重紋理混合,應(yīng)用程序應(yīng)該在進(jìn)行光照貼圖時(shí)利用這一特性,這將極大地提高應(yīng)用程序的性能。

            如果使用光照貼圖,Direct3D應(yīng)用程序可以在渲染圖元時(shí)得到多種光照效果。應(yīng)用程序不僅可以在場(chǎng)景中使用單色光和有色光,還可以添加諸如鏡面反射高光和漫反射光之類的細(xì)節(jié)。

            以下主題介紹了用Direct3D紋理混合實(shí)現(xiàn)光照貼圖的信息。


            單色光照貼圖


            一些老的三維加速卡不支持使用目標(biāo)像素的阿爾法值進(jìn)行紋理混合,更多信息請(qǐng)參閱阿爾法紋理混合。一般來(lái)說(shuō)這些加速卡也不支持多重紋理混合,如果應(yīng)用程序在此類適配器上運(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ù)。


            有色光照貼圖


            如果應(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ù)。


            鏡面反射光照貼圖


            在對(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 );


            漫反射光照貼圖


            不光滑的表面在被光源照射時(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 );


            表面


            表面是顯存的線性表示,雖然可以存在于系統(tǒng)內(nèi)存中,但更通常都存在于顯卡的顯存中。IDirect3DSurface9接口包含了表面對(duì)象。

            可以通過(guò)調(diào)用下列方法得到一個(gè)IDirect3DSurface9接口。

            • IDirect3DCubeTexture9::GetCubeMapSurface
            • IDirect3DDevice9::CreateDepthStencilSurface
            • IDirect3DDevice9::CreateRenderTarget
            • IDirect3DDevice9::GetBackBuffer
            • IDirect3DDevice9::GetDepthStencilSurface
            • IDirect3DDevice9::GetFrontBufferData
            • IDirect3DDevice9::GetRenderTarget
            • IDirect3DSwapChain9::GetBackBuffer
            • IDirect3DTexture9::GetSurfaceLevel

            IDirect3DSurface9接口允許應(yīng)用程序通過(guò)IDirect3DDevice9::UpdateSurface方法間接訪問內(nèi)存。該方法允許應(yīng)用程序從一個(gè)IDirect3DSurface9接口復(fù)制一塊矩形區(qū)域的像素到另一個(gè)IDirect3DSurface9接口。表面接口也提供了直接訪問顯存的方法。例如,應(yīng)用程序可以用IDirect3DSurface9::LockRect方法鎖定顯存的一塊矩形區(qū)域。很重要的一點(diǎn)是在完成對(duì)鎖定的矩形區(qū)域的操作后,要調(diào)用IDirect3DSurface9::UnlockRect方法。

            表面格式用于描述如何解釋表面內(nèi)存中的像素?cái)?shù)據(jù)。Microsoft® Direct3D®使用D3DSURFACE_DESC結(jié)構(gòu)的D3DFORMAT成員描述表面格式。應(yīng)用程序可以通過(guò)調(diào)用IDirect3DSurface9::GetDesc方法取得一個(gè)現(xiàn)有的表面的格式。

            以下主題包含了更多信息。


            寬度與Pitch的比較


            雖然對(duì)術(shù)語(yǔ)寬度和pitch的使用經(jīng)常不很正式,但它們有著非常重要并且完全不同的意義。因此,開發(fā)人員應(yīng)該理解它們的意義,以及如何解釋Microsoft® Direct3D®用于描述它們的值。

            Direct3D使用D3DSURFACE_DESC結(jié)構(gòu)保存描述表面的信息。其中,這個(gè)結(jié)構(gòu)包含了表面的大小,以及這些大小在內(nèi)存中是如何表示的。結(jié)構(gòu)使用HeightWidth成員描述表面的邏輯大小,這兩個(gè)成員都以像素為單位。因此,對(duì)于一個(gè)640x480表面來(lái)說(shuō),無(wú)論它是8位表面或24位RGB表面,它的HeightWidth值都是相同的。

            當(dāng)應(yīng)用程序使用IDirect3DSurface9::LockRect方法鎖定一個(gè)表面時(shí),該方法會(huì)填寫一個(gè)D3DLOCKED_RECT結(jié)構(gòu),這個(gè)結(jié)構(gòu)包含了表面的pitch值及一個(gè)指向被鎖定數(shù)據(jù)的指針。Pitch成員的值描述了表面在內(nèi)存中的pitch,也被稱為跨度。Pitch是兩個(gè)內(nèi)存地址間以字節(jié)為單位的距離,兩個(gè)內(nèi)存地址分別表示一個(gè)位圖某一行的起始地址以及下一行的起始地址。因?yàn)閜itch是以字節(jié)為單位而非以像素為單位,所以一個(gè)640x480x8表面的pitch值與另一個(gè)大小相同但像素格式不同的表面的pitch值會(huì)大不相同。另外,pitch值有時(shí)反映出被Direct3D保留并用作高速緩存的字節(jié)數(shù),因此簡(jiǎn)單地認(rèn)為pitch就是寬度乘以每個(gè)像素所占的字節(jié)數(shù)是不保險(xiǎn)的。如下圖所示,對(duì)寬度和pitch之間的區(qū)別做一個(gè)直觀的比較會(huì)更清楚。

             

            在這張圖中,前后緩存都是640x480x8,高速緩存為384x480x8。

            當(dāng)直接訪問表面時(shí)要小心,不要訪問為表面分配的內(nèi)存以外的地方,更不要訪問任何為高速緩存目的而保留的內(nèi)存。另外,當(dāng)應(yīng)用程序鎖定一個(gè)表面的一部分時(shí),應(yīng)用程序必須保持在鎖定表面時(shí)指定的矩形區(qū)域內(nèi)。不按這些指導(dǎo)方針行事將會(huì)導(dǎo)致無(wú)法預(yù)料的結(jié)果。當(dāng)直接渲染到表面內(nèi)存時(shí),應(yīng)該總是使用由IDirect3DSurface9::LockRect方法返回的pitch值。不要認(rèn)為pitch僅取決于顯示模式。如果應(yīng)用程序在一些顯示適配器上運(yùn)行良好,但在另一些適配器上顯示不正確的話,很可能就是pitch的問題。

            更多信息,請(qǐng)參閱直接訪問表面內(nèi)存


            翻轉(zhuǎn)表面


            Microsoft® Direct3D®應(yīng)用程序一般通過(guò)這種方式顯示動(dòng)畫序列,即:先在后緩存中生成動(dòng)畫的各幀,然后按順序把這些幀顯示出來(lái)。后緩存是屬于交換鏈的一部分。一個(gè)交換鏈?zhǔn)且幌盗泻缶彺妫@些后緩存會(huì)一個(gè)接一個(gè)被“翻轉(zhuǎn)”到屏幕上。這種方法可以用來(lái)在內(nèi)存中渲染一個(gè)場(chǎng)景,當(dāng)渲染完成后隨即把場(chǎng)景翻轉(zhuǎn)到屏幕上。這避免了畫面撕裂的現(xiàn)象,并能生成更為平滑的動(dòng)畫。

            Direct3D中創(chuàng)建的每個(gè)設(shè)備至少有一個(gè)交換鏈。當(dāng)應(yīng)用程序初始化第一個(gè)Direct3D設(shè)備時(shí),應(yīng)用程序要設(shè)置D3DPRESENT_PARAMETER結(jié)構(gòu)的BackBufferCount成員,告訴Direct3D交換鏈需要包含的后緩存的數(shù)量。隨后對(duì)IDirect3DDevice9::CreateDevice的調(diào)用會(huì)創(chuàng)建Direct3D設(shè)備及相應(yīng)的交換鏈。

            當(dāng)應(yīng)用程序使用IDirect3DDevice9::Present方法要求一個(gè)翻轉(zhuǎn)操作時(shí),指向前緩存的指針和指向后緩存的指針被交換。翻轉(zhuǎn)是通過(guò)切換顯示設(shè)備用來(lái)引用內(nèi)存的指針完成的,而不是復(fù)制表面的內(nèi)存。當(dāng)翻轉(zhuǎn)鏈包含一個(gè)前緩存和一個(gè)以上的后緩存時(shí),指針的切換以循環(huán)的方式進(jìn)行,如下圖所示。

             

            通過(guò)調(diào)用IDirect3DDevice9::CreateAdditionalSwapChain,應(yīng)用程序可以為設(shè)備創(chuàng)建附加的交換鏈。應(yīng)用程序可以為每個(gè)視區(qū)創(chuàng)建一個(gè)交換鏈并將每個(gè)交換鏈與某個(gè)特定窗口相關(guān)聯(lián)。應(yīng)用程序在每個(gè)交換鏈的后緩存中渲染圖像,然后分別顯示它們。IDirect3DDevice9::CreateAdditionalSwapChain的兩個(gè)參數(shù)分別為一個(gè)指向D3DPRESENT_PARAMETER結(jié)構(gòu)的指針和一個(gè)指向IDirect3DSwapChain9接口的指針。應(yīng)用程序可以使用IDirect3DSwapChain9::Present顯示位于前緩存之后的那個(gè)后緩存的內(nèi)容。注意一個(gè)設(shè)備只能有一個(gè)全屏交換鏈。

            應(yīng)用程序可以通過(guò)調(diào)用IDirect3DDevice9::GetBackBufferIDirect3DSwapChain9::GetBackBuffer方法取得對(duì)某個(gè)后緩存的訪問權(quán),這兩個(gè)方法會(huì)返回一個(gè)指向IDirect3DSurface9接口的指針,代表被返回的后緩存表面。注意對(duì)這兩個(gè)方法的調(diào)用會(huì)增加IDirect3DDevice9接口的內(nèi)部引用計(jì)數(shù),因此當(dāng)完成對(duì)表面的操作后要記得調(diào)用IUnknown::Release,否則會(huì)導(dǎo)致內(nèi)存泄漏。

            記住,Direct3D通過(guò)交換交換鏈內(nèi)指向表面內(nèi)存的指針翻轉(zhuǎn)表面,而不是交換表面本身。這意味著應(yīng)用程序總是在下次將被顯示的那個(gè)后緩存上進(jìn)行渲染。

            很重要的一點(diǎn)是要注意由顯卡驅(qū)動(dòng)程序執(zhí)行的“翻轉(zhuǎn)操作”和一個(gè)用D3DSWAPCHAIN_FLIP標(biāo)志創(chuàng)建的交換鏈執(zhí)行的“present”操作間的區(qū)別。

            按照慣例,術(shù)語(yǔ)“翻轉(zhuǎn)”表示改變顯卡用來(lái)產(chǎn)生輸出信號(hào)的視頻內(nèi)存地址的范圍,這樣就導(dǎo)致原先隱藏著的后緩存的內(nèi)容將被顯示。在Microsoft DirectX® 9.0中,這個(gè)術(shù)語(yǔ)更經(jīng)常地是被用來(lái)描述把任何用D3DSWAPEFFECT_FLIP標(biāo)志創(chuàng)建的交換鏈中的后緩存顯示出來(lái)。

            而當(dāng)交換鏈為全屏模式時(shí),“present”操作幾乎總是通過(guò)翻轉(zhuǎn)操作實(shí)現(xiàn),當(dāng)交換鏈為窗口模式時(shí),“present”操作必然通過(guò)復(fù)制操作實(shí)現(xiàn)。此外,顯卡驅(qū)動(dòng)程序可能會(huì)根據(jù)D3DSWAPEFFECT_DISCARD和D3DSWAPEFFECT_COPY標(biāo)志,用翻轉(zhuǎn)實(shí)現(xiàn)全屏交換鏈的present操作。

            以上討論適用于常用的情況,也就是用D3DSWAPEFFECT_FLIP標(biāo)志創(chuàng)建的全屏交換鏈。

            有關(guān)窗口和全屏交換鏈的各種不同交換效果的討論,請(qǐng)參閱D3DSWAPEFFECT


            頁(yè)面翻轉(zhuǎn)和后緩存


            頁(yè)面翻轉(zhuǎn)是多媒體、動(dòng)畫和游戲軟件中的關(guān)鍵,它和動(dòng)畫師用一疊紙產(chǎn)生動(dòng)畫的方法相似。在每張紙上,動(dòng)畫師對(duì)圖片稍做改變,因此當(dāng)動(dòng)畫師在頁(yè)與頁(yè)之間快速地翻動(dòng)時(shí),圖片看起來(lái)就像是動(dòng)了。

            軟件中的頁(yè)面翻轉(zhuǎn)與這個(gè)過(guò)程相似。Microsoft® Direct3D®通過(guò)交換鏈實(shí)現(xiàn)頁(yè)面翻轉(zhuǎn)功能,而交換鏈?zhǔn)窃O(shè)備的一個(gè)屬性。開始時(shí),應(yīng)用程序先設(shè)置一系列Direct3D緩存,這些緩存會(huì)以和動(dòng)畫師相同的翻頁(yè)方法翻轉(zhuǎn)到屏幕。第一個(gè)緩存被稱為前顏色緩存,它之后的緩存被稱為后緩存。應(yīng)用程序可以先寫入到后緩存,然后翻轉(zhuǎn)顏色緩存,這樣后緩存就顯示在屏幕上。當(dāng)系統(tǒng)顯示圖像時(shí),應(yīng)用程序又可以寫入到后緩存。這個(gè)過(guò)程可以一直持續(xù),這樣應(yīng)用程序就可以高效地生成活動(dòng)的圖像。

            Direct3D使建立一個(gè)頁(yè)面翻轉(zhuǎn)機(jī)制非常容易——從一個(gè)雙緩存機(jī)制(一個(gè)前顏色緩存和一個(gè)后緩存)到使用額外后緩存的更為復(fù)雜的機(jī)制。


            復(fù)制到表面


            當(dāng)使用IDirect3DDevice9::UpdateSurface時(shí),要傳入源表面中的一個(gè)矩形,或者用NULL表示整個(gè)表面,應(yīng)用程序還需要傳入目標(biāo)表面中的一個(gè)點(diǎn),源圖像的左上角將被復(fù)制到這個(gè)位置。該方法不支持裁剪,除非源矩形和對(duì)應(yīng)的目標(biāo)矩形分別完全被包含在源和目標(biāo)表面內(nèi),否則操作將會(huì)失敗。這個(gè)方法不支持阿爾法混合、color key,及格式轉(zhuǎn)換。注意目標(biāo)表面和源表面不能是同一個(gè)表面。

            其它有關(guān)UpdateSurface的使用限制,請(qǐng)參閱IDirect3DDevice9::UpdateSurface

            C++/C應(yīng)用程序可以用下列方法把圖像復(fù)制到一個(gè)Microsoft® Direct3D®表面。

            • D3DXLoadSurfaceFromFile
            • D3DXLoadSurfaceFromFileInMemory
            • D3DXLoadSurfaceFromMemory
            • D3DXLoadSurfaceFromResource
            • D3DXLoadSurfaceFromSurface
            • IDirect3DDevice9::UpdateSurface

            相關(guān)主題

            • IDirect3DDevice9::StretchRect

            復(fù)制表面


            術(shù)語(yǔ)blit是“位塊傳輸(bit block transfer)”的縮寫,表示把數(shù)據(jù)塊從內(nèi)存中的一處傳輸?shù)搅硪惶幍倪^(guò)程。作為在每幀——IDirect3DDevice9::Present方法背后的面向復(fù)制的機(jī)制——中移動(dòng)大塊矩形中的像素的主要機(jī)制,blitting設(shè)備驅(qū)動(dòng)程序接口(DDI)仍在繼續(xù)使用。blit操作中對(duì)紋理數(shù)據(jù)的傳輸由IDirect3DDevice9::UpdateTexture方法執(zhí)行。在DirectX 9.0中,紋理數(shù)據(jù)也可以用IDirect3DDevice9::UpdateSurface方法復(fù)制,該方法復(fù)制像素的一個(gè)矩形子集。

            注意 DirectX 9.0提供了Direct3D擴(kuò)展(D3DX)函數(shù),這使應(yīng)用程序可以從文件載入紋理,進(jìn)行顏色轉(zhuǎn)換,及調(diào)整紋理的大小。有關(guān)更多可供使用的函數(shù)的信息,請(qǐng)參閱與紋理相關(guān)的函數(shù)

            相關(guān)主題

            • IDirect3DDevice9::StretchRect

            直接訪問表面內(nèi)存


            通過(guò)使用IDirect3DSurface9::LockRect方法,應(yīng)用程序可以直接訪問表面內(nèi)存。在調(diào)用這個(gè)方法時(shí),pRect參數(shù)為指向RECT結(jié)構(gòu)的指針,描述要直接訪問表面上的哪一部分。如果要鎖定整個(gè)表面,只需把pRect設(shè)為NULL即可。同時(shí),應(yīng)用程序可以指定一個(gè)只覆蓋表面的一部分的RECT。如果提供兩個(gè)不相交迭的矩形,那么兩個(gè)線程或進(jìn)程可以同時(shí)鎖定一個(gè)表面中的多個(gè)矩形。注意一個(gè)多重取樣的(multisample)后緩存不能被鎖定。

            IDirect3DSurface9::LockRect方法會(huì)填寫一個(gè)D3DLOCKED_RECT結(jié)構(gòu),該結(jié)構(gòu)中包含了訪問表面內(nèi)存所需的全部信息。該結(jié)構(gòu)包含了pitch信息,及一個(gè)指向被鎖定的數(shù)據(jù)的指針。當(dāng)應(yīng)用程序完成對(duì)表面內(nèi)存的訪問后,應(yīng)該調(diào)用IDirect3DSurface9::UnlockRect方法將表面解鎖。

            應(yīng)用程序在鎖定了一個(gè)表面后,可以直接對(duì)其中的內(nèi)容進(jìn)行操作。下面給出了一些提示,說(shuō)明如何避免在使用直接渲染表面內(nèi)存(directly rendering surface memory)時(shí)遇到的一些問題。

            • 絕對(duì)不要認(rèn)為pitch是一個(gè)常數(shù),應(yīng)該總是檢查IDirect3DSurface9::LockRect方法返回的pitch信息。Pitch可能會(huì)因?yàn)楦鞣N原因而不同,包括表面內(nèi)存所在的位置,顯卡的類型,甚至是Microsoft® Direct3D®驅(qū)動(dòng)程序的版本。更多信息請(qǐng)參閱寬度與pitch的比較
            • 應(yīng)用程序應(yīng)該保證只對(duì)未鎖定的表面進(jìn)行復(fù)制操作,如果是鎖定的表面,那么Direct3D的復(fù)制操作將會(huì)失敗。
            • 當(dāng)一個(gè)表面被鎖定時(shí),應(yīng)用程序應(yīng)該限制對(duì)它進(jìn)行的操作。
            • 在復(fù)制數(shù)據(jù)時(shí),應(yīng)用程序應(yīng)該總是保證和顯存對(duì)齊。Microsoft Windows® 98使用了一個(gè)頁(yè)故障處理器,Vflatd.386,它為使用內(nèi)存單元切換(bank-switched memory)的顯卡實(shí)現(xiàn)一個(gè)虛擬的平面幀緩存。該處理器允許此類顯示設(shè)備以線性方式把幀緩存提供給Direct3D。當(dāng)復(fù)制與顯存不對(duì)齊的數(shù)據(jù)時(shí),如果復(fù)制的數(shù)據(jù)跨越內(nèi)存單元,那么可能會(huì)導(dǎo)致系統(tǒng)掛起。
            • 如果表面隸屬于D3DPOOL_DEFAULT內(nèi)存池中的資源,那么它可能無(wú)法被鎖定,除非它是動(dòng)態(tài)紋理或是用IDirect3DDevice9::CreateOffscreenPlainSurface創(chuàng)建的表面。后緩存表面可以通過(guò)IDirect3DDevice9::GetBackBufferIDirect3DSwapChain::GetBackBuffer方法訪問,只有在創(chuàng)建交換鏈時(shí),D3DPRESENT_PARAMETER結(jié)構(gòu)的Flags成員包含了D3DPRESENT_LOCKABLE_BACKBUFFER標(biāo)志的情況下,它們才可以被鎖定。

            私有表面數(shù)據(jù)


            應(yīng)用程序可以在表面中存儲(chǔ)任何類型的應(yīng)用程序特有的數(shù)據(jù)。例如,在一個(gè)游戲中,一個(gè)表示地圖的表面可以包含有關(guān)地形的數(shù)據(jù)。

            一個(gè)表面可以有一個(gè)以上的私有數(shù)據(jù)緩存。每個(gè)緩存用一個(gè)GUID標(biāo)識(shí),該GUID由應(yīng)用程序在把數(shù)據(jù)連接到表面時(shí)提供。

            要存儲(chǔ)私有表面數(shù)據(jù),應(yīng)該使用SetPrivateData,并傳入源緩存,數(shù)據(jù)的大小,及應(yīng)用程序?yàn)閿?shù)據(jù)定義的GUID。或者,源數(shù)據(jù)也可以以COM對(duì)象的形式存在,這種情況下,應(yīng)用程序只需傳入對(duì)象的IUnknown接口指針,并設(shè)置D3DSPD_IUNKNOWNPOINTER標(biāo)志。

            SetPrivateData會(huì)為數(shù)據(jù)分配一塊內(nèi)部緩存并把數(shù)據(jù)復(fù)制到其中。應(yīng)用程序可以安全地釋放源緩存或?qū)ο蟆.?dāng)FreePrivateData被調(diào)用時(shí),內(nèi)部緩存或?qū)涌诘囊靡矔?huì)被釋放。當(dāng)釋放一個(gè)表面時(shí),系統(tǒng)會(huì)自動(dòng)執(zhí)行這個(gè)操作。

            要得到表面的私有數(shù)據(jù),應(yīng)用程序必須先分配一塊大小合適的緩存,然后調(diào)用GetPrivateData方法,并把原先賦給數(shù)據(jù)的GUID傳入。應(yīng)用程序有責(zé)任釋放任何用于這塊緩存的動(dòng)態(tài)內(nèi)存。如果數(shù)據(jù)是COM對(duì)象,那么該方法會(huì)取得IUnknown指針。

            如果應(yīng)用程序不知道應(yīng)該分配多大的緩存,可以先把pSizeOfData設(shè)為零并調(diào)用GetPrivateData,如果調(diào)用失敗并返回D3DERR_MOREDATA,那么該方法會(huì)在pSizeOfData中返回所需的字節(jié)數(shù)。


            Gamma控制


            Gamma控制允許應(yīng)用程序改變系統(tǒng)如何顯示表面的內(nèi)容,同時(shí)不會(huì)影響表面本身的內(nèi)容。可以認(rèn)為這些控制是很簡(jiǎn)單的過(guò)濾器,在把表面數(shù)據(jù)顯示在屏幕上之前,Microsoft® Direct3D®會(huì)對(duì)這些數(shù)據(jù)進(jìn)行過(guò)濾。

            Gamma控制是交換鏈的一個(gè)屬性。有了Gamma控制,動(dòng)態(tài)改變?nèi)绾伟驯砻娴募t、綠和藍(lán)色深映射到系統(tǒng)最終顯示的實(shí)際色深就成為了可能。通過(guò)設(shè)置Gamma level,應(yīng)用程序可以使用戶的屏幕閃現(xiàn)不同的顏色——當(dāng)用戶控制的角色被擊中時(shí)為紅色,當(dāng)角色撿起了新的物品時(shí)為綠色,等等——同時(shí)不必為了達(dá)到相同的效果而把新的圖像復(fù)制到幀緩存中去。

            由于在Microsoft DirectX® 9.0中,交換鏈?zhǔn)窃O(shè)備的一個(gè)屬性,因此每個(gè)Direct3D設(shè)備都至少有一條交換鏈(隱式交換鏈)。正因?yàn)間amma ramp是交換鏈的一個(gè)屬性,所以當(dāng)交換鏈處于窗口模式下時(shí),gamma ramp也可以使用。Gamma ramp會(huì)立刻生效,不存在等待VSYNC的操作。

            IDirect3DDevice9::SetGammaRampIDirect3DDevice9::GetGammaRamp方法允許應(yīng)用程序在把表面中的像素送到數(shù)模轉(zhuǎn)換器(DAC)進(jìn)行顯示之前對(duì)ramp levels進(jìn)行操作,這會(huì)影響到表面中的像素的紅、綠和藍(lán)色分量。

            Gamma Ramp Levels

            Direct3D中,術(shù)語(yǔ)gamma ramp指的是一個(gè)組數(shù)值,這些數(shù)值用于把幀緩存中所有像素的某一顏色分量——紅、綠、藍(lán)——的level映射到被DAC接收并用于顯示的新的色深。

            以下是gamma ramp的工作方式:Direct3D從幀緩存中得到一個(gè)像素并分別計(jì)算每個(gè)紅、綠和藍(lán)顏色分量。每個(gè)分量由一個(gè)位于0到65535之間的值表示。Direct3D用這個(gè)原始值作為索引,在一個(gè)有256個(gè)元素的數(shù)組(即ramp)中查找,數(shù)組中每個(gè)元素包含一個(gè)值,用來(lái)替換原始值。Direct3D對(duì)幀緩存中每個(gè)像素的每個(gè)顏色分量進(jìn)行這個(gè)查找并替換的過(guò)程,從而改變了所有最終顯示在屏幕上的像素的顏色。

            通過(guò)畫圖很容易就能把ramp值直觀地表示出來(lái)。下面兩張圖中左圖顯示了一個(gè)完全不改變顏色的ramp,右圖顯示的ramp會(huì)給它所作用于的顏色分量加上負(fù)偏移。

             

            左圖中數(shù)組元素包含的值與它們的索引值相同——索引為0的元素的值為0,索引為255的元素的值為65535。這是默認(rèn)的ramp類型,它不會(huì)在顯示輸入值之前改變它們。右圖顯示的ramp有較大變化,第一個(gè)元素的值為0,最后一個(gè)元素的值為32768,第一個(gè)和最后一個(gè)元素之間的元素的值在0到32768之間均勻分布。得到的效果就是使用這個(gè)ramp的顏色分量在顯示器上會(huì)顯得比較暗。Direct3D并沒有限制必須使用線性映射,如果需要,應(yīng)用程序可以指定任意的映射方式。應(yīng)用程序甚至可以把所有元素都設(shè)為零,以把某一顏色分量從顯示器上完全消除。

            設(shè)置及取得Gamma Ramp Levels

            Gamma ramp levels是Direct3D用于把幀緩存中的顏色分量映射到將被顯示新的level的快速查找表。應(yīng)用程序可以通過(guò)調(diào)用IDirect3DDevice9::SetGammaRampIDirect3DDevice9::GetGammaRamp方法設(shè)置和取得主表面的ramp levels。IDirect3DDevice9::SetGammaRamp接收兩個(gè)參數(shù),第一個(gè)參數(shù)為D3DSGR_CALIBRATE或D3DSGR_NO_CALIBRATION,第二個(gè)參數(shù)pRamp為一指向D3DGAMMARAMP結(jié)構(gòu)的指針。D3DGAMMARAMP結(jié)構(gòu)包含了三個(gè)有256個(gè)元素的WORD數(shù)組,每個(gè)數(shù)組用來(lái)存放紅、綠和藍(lán)gamma ramp。IDirect3DDevice9::GetGammaRamp接收一個(gè)參數(shù),為一指向D3DGAMMARAMP的指針,當(dāng)前的gamma ramp將被填寫到該指針指向的結(jié)構(gòu)中。

            應(yīng)用程序可以把IDirect3DDevice9::SetGammaRamp的第一個(gè)參數(shù)設(shè)為DDSGR_CALIBRATE,這樣在設(shè)置新的gamma levels時(shí)就會(huì)調(diào)用校正器。由于校正gamma ramp會(huì)增加一些開銷,因此最好不要頻繁地調(diào)用。無(wú)論顯卡或顯示器是何類型,設(shè)置一個(gè)校正過(guò)的gamma ramp會(huì)給用戶提供完全一致的gamma值。

            并非所有系統(tǒng)都支持gamma校正,要測(cè)定設(shè)備是否支持gamma校正,應(yīng)該調(diào)用IDirect3DDevice9::GetDeviceCaps,然后檢查由該方法返回的D3DCAPS9結(jié)構(gòu)的Caps2成員。如果設(shè)置了D3DCAPS2_CANCALIBRATEGAMMA能力標(biāo)志,那么設(shè)備就支持gamma校正。

            在設(shè)置新的ramp levels時(shí),謹(jǐn)記應(yīng)用程序在數(shù)組中設(shè)置的levels只有當(dāng)應(yīng)用程序運(yùn)行于全屏獨(dú)占模式時(shí)才會(huì)被使用。一旦應(yīng)用程序切換到正常模式,ramp levels就會(huì)被忽略,并在應(yīng)用程序恢復(fù)到全屏模式時(shí)重新生效。

            即使設(shè)備在當(dāng)前presentation模式(全屏或窗口)下不支持gamma ramps,也不會(huì)返回錯(cuò)誤碼。應(yīng)用程序可以檢查D3DCAPS9結(jié)構(gòu)的Caps2成員是否設(shè)置了D3DCAPS2_FULLSCREENGAMMA和D3DCAPS2_CANCALIBRATEGAMMA能力位,以確定設(shè)備的能力及是否安裝了校正器。


            Mipmap的自動(dòng)生成


            Mipmap的自動(dòng)生成在創(chuàng)建紋理時(shí)利用了硬件過(guò)濾,這使得這項(xiàng)功能對(duì)應(yīng)用程序而言是完全透明的。自動(dòng)生成對(duì)mipmap渲染目標(biāo)尤其有用,因?yàn)樗鼈兾挥陲@存中,而這種情況下,軟件過(guò)濾的效率很低。

            要自動(dòng)生成 mipmap,應(yīng)該在創(chuàng)建紋理時(shí)設(shè)置D3DUSAGE_AUTOGENMIPMAP標(biāo)志。后續(xù)的sublevel的生成對(duì)應(yīng)用程序來(lái)說(shuō)都是完全透明的。在某些情況下,某些硬件的自動(dòng)mipmap生成可能會(huì)占用許多時(shí)間,這時(shí)應(yīng)用程序可以適時(shí)地使用IDirect3DBaseTexture9::GenerateMipSubLevels,示意驅(qū)動(dòng)程序生成相應(yīng)數(shù)量的sublevels。

            IDirect3DBaseTexture9::SetAutoGenFilterType用來(lái)控制自動(dòng)生成時(shí)的過(guò)濾質(zhì)量。改變過(guò)濾類型會(huì)導(dǎo)致mipmap的sublevels被標(biāo)記為無(wú)效的并需要重新生成。

            IDirect3DBaseTexture9::GetAutoGenFilterType用來(lái)取得當(dāng)前的過(guò)濾類型。在創(chuàng)建紋理時(shí)使用的默認(rèn)過(guò)濾類型是D3DTEXF_LINEAR。如果驅(qū)動(dòng)程序不支持線性過(guò)濾,那么過(guò)濾類型將被設(shè)為D3DTEXF_POINT

            如果紋理不是用D3DUSAGE_AUTOGENMIPMAP標(biāo)志創(chuàng)建的,那么調(diào)用這些方法不會(huì)產(chǎn)生任何效果,也不會(huì)返回任何錯(cuò)誤。除了D3DTEXF_NONE外,驅(qū)動(dòng)程序支持的所有可用于常規(guī)紋理過(guò)濾的過(guò)濾類型都可用于自動(dòng)生成。對(duì)每種資源類型而言,驅(qū)動(dòng)程序應(yīng)該支持它在相應(yīng)的紋理、立方體紋理和立體紋理過(guò)濾能力信息中提供的過(guò)濾類型。

            當(dāng)源紋理是自動(dòng)生成的mipmap,而目標(biāo)紋理不是時(shí),IDirect3DDevice9::UpdateTexture是非法的,調(diào)用會(huì)失敗。如果源紋理不是自動(dòng)生成的mipmap且目標(biāo)紋理是自動(dòng)生成的mipmap,那么只有目標(biāo)紋理中的最高的相匹配的那層被更新,該層的sublevels會(huì)重新被生成,而源紋理的sublevels將會(huì)被忽略。與此類似,如果源紋理和目標(biāo)紋理都是自動(dòng)生成的mipmap,那么只有目標(biāo)紋理中的最高的相匹配的那層被更新,該層的sublevels會(huì)被重新生成,而源紋理的sublevels將被忽略。

            在創(chuàng)建一個(gè)自動(dòng)生成的mipmap時(shí),Levels參數(shù)必須被設(shè)置為零或一。

            要檢查設(shè)備對(duì)自動(dòng)mipmap生成的支持,應(yīng)該檢查設(shè)備是否設(shè)置了D3DCAPS2_CANAUTOGENMIPMAP能力位。如果是,那么應(yīng)該用D3DUSAGE_AUTOGENMIPMAP作為參數(shù)調(diào)用IDirect3D9::CheckDeviceFormat方法。如果返回值為D3D_OK,那么可以保證mipmap是自動(dòng)生成的。如果返回值是D3DOK_NOAUTOGEN,這意味著對(duì)創(chuàng)建紋理的調(diào)用會(huì)成功,但不會(huì)生成任何mipmap。

            知道設(shè)備支持哪些過(guò)濾類型,應(yīng)該檢查D3DCAPS9結(jié)構(gòu)的TextureFilterCapsCubeTextureFilterCaps(譯注:及VolumeTextureFilterCaps)成員所包含的能力位。

            最后,要注意D3DUSAGE_AUTOGENMIPMAP只是一個(gè)提示,在創(chuàng)建紋理或調(diào)用 IDirect3D9::CheckDeviceFormat的過(guò)程中指定這個(gè)標(biāo)志不會(huì)在任何類型的設(shè)備驅(qū)動(dòng)程序接口(DDI)上引起錯(cuò)誤。

            相關(guān)主題

            • D3DUSAGE
            • D3DCAPS2
            • D3DTEXTUREFILTERTYPE

            自動(dòng)紋理管理


            紋理管理是確定在某一特定時(shí)刻需要用哪些紋理進(jìn)行渲染,并確保這些紋理已經(jīng)被載入顯存的過(guò)程。同任何算法一樣,不同的紋理管理機(jī)制在復(fù)雜度上會(huì)有所不同,但任何紋理管理機(jī)制都會(huì)涉及到以下一些關(guān)鍵任務(wù)。

            • 跟蹤可用顯存的數(shù)量。
            • 計(jì)算哪些紋理需要被用于渲染,而哪些不需要。
            • 確定哪些現(xiàn)存(于顯存中)的紋理資源可以重新載入其它紋理圖像,以及哪些表面應(yīng)該被銷毀并被新的紋理資源代替。

            為了保證紋理載入具有最佳的性能,Microsoft® Direct3D®內(nèi)建了對(duì)紋理管理的支持。由Direct3D管理的紋理資源被稱為由系統(tǒng)管理的資源

            紋理管理器用時(shí)間戳對(duì)紋理進(jìn)行跟蹤,時(shí)間戳記錄了紋理最后被使用的時(shí)間。管理器然后用最近最少使用(least-recently-used)算法確定哪些紋理應(yīng)該被移除。在準(zhǔn)備把兩張紋理從顯存中移除時(shí),紋理的優(yōu)先級(jí)用來(lái)仲裁。如果兩張紋理具有相同的優(yōu)先級(jí),那么最近最少使用的那張紋理會(huì)被移除。如果兩張紋理具有相同的時(shí)間戳,那么優(yōu)先級(jí)較低的那張紋理會(huì)先被移除。

            應(yīng)用程序可以在創(chuàng)建紋理表面時(shí)要求自動(dòng)紋理管理。要在C++應(yīng)用程序中得到一個(gè)由系統(tǒng)管理的紋理,應(yīng)該調(diào)用IDirect3DDevice9::CreateTexture創(chuàng)建紋理資源,并把Pool參數(shù)指定為D3DPOOL_MANAGED。Direct3D不允許應(yīng)用程序指定要在何處創(chuàng)建紋理。在創(chuàng)建由系統(tǒng)管理的紋理時(shí),應(yīng)用程序不能使用D3DPOOL_DEFAULT或D3DPOOL_SYSTEMMEM標(biāo)志。創(chuàng)建完由系統(tǒng)管理的紋理后,應(yīng)用程序可以調(diào)用IDirect3DDevice9::SetTexture方法把紋理設(shè)到渲染設(shè)備的紋理級(jí)聯(lián)中。

            應(yīng)用程序可以通過(guò)調(diào)用IDirect3DDevice9::SetPriority方法給由系統(tǒng)管理的紋理設(shè)置優(yōu)先級(jí)。

            Direct3D根據(jù)需要自動(dòng)把紋理載入顯存。系統(tǒng)可能會(huì)根據(jù)非本地視頻內(nèi)存的可用性或其它因素把由系統(tǒng)管理的紋理放在本地或非本地視頻內(nèi)存中作為高速緩存。系統(tǒng)不會(huì)把由系統(tǒng)管理的紋理所用的緩存的位置和大小告訴應(yīng)用程序,而且對(duì)使用自動(dòng)紋理管理而言也無(wú)需了解該信息。如果應(yīng)用程序使用的紋理超過(guò)了顯存所能容納的數(shù)量,那么Direct3D會(huì)把舊的紋理從顯存中移除以給新的紋理騰出空間。如果應(yīng)用程序再次用到被移除的紋理,那么系統(tǒng)會(huì)用原始的系統(tǒng)內(nèi)存紋理表面把紋理重新載入到顯存的高速緩存中。雖然重新載入紋理是必須的,但它同時(shí)降低了應(yīng)用程序的性能。

            通過(guò)更新或鎖定紋理資源,應(yīng)用程序可以動(dòng)態(tài)地修改紋理位于系統(tǒng)內(nèi)存中的原件。當(dāng)系統(tǒng)檢測(cè)到一個(gè)無(wú)效表面時(shí)——在更新操作完成后,或當(dāng)表面被解鎖時(shí)——紋理管理器會(huì)自動(dòng)地更新紋理位于顯存中的復(fù)本。由此導(dǎo)致的性能下降與重新載入一個(gè)被移除的紋理相似。

            當(dāng)進(jìn)入游戲中新的一關(guān)時(shí),應(yīng)用程序可能需要清空顯存中所有由系統(tǒng)管理的紋理,此時(shí)應(yīng)該調(diào)用IDirect3DDevice9::EvictManagedResources

            有關(guān)資源管理的更多信息,請(qǐng)參閱管理資源


            壓縮紋理資源


            紋理貼圖是畫在三維物體上的數(shù)字化圖像,用來(lái)添加可視細(xì)節(jié)。它們?cè)诠鈻呕瘯r(shí)被貼到物體表面,這個(gè)過(guò)程會(huì)消耗大量的系統(tǒng)帶寬和內(nèi)存。為了減少紋理所消耗內(nèi)存的數(shù)量,Microsoft® Direct3D®支持對(duì)紋理表面的壓縮。一些Direct3D設(shè)備本身就支持壓縮紋理表面。在這些設(shè)備上,只要應(yīng)用程序創(chuàng)建了壓縮表面并將數(shù)據(jù)載入其中,該表面就可以和其它任何紋理表面一樣,在Direct3D中使用。在把壓縮紋理貼到三維物體表面時(shí),Direct3D會(huì)進(jìn)行解壓。

            存儲(chǔ)效率和紋理壓縮

            所有紋理壓縮的格式都是二的乘方。雖然這并不表示紋理一定要是方的,但確實(shí)表示X和Y都是二的乘方。例如,如果一個(gè)紋理原來(lái)是512×128,那下一級(jí)mipmap應(yīng)該是256×64,依次類推,每一級(jí)都以兩倍遞減。到最低兩級(jí),紋理被過(guò)濾成16×2 和8×1,因?yàn)閴嚎s塊總是一個(gè)4×4的texel塊,所以這里會(huì)浪費(fèi)一些數(shù)據(jù)位。塊中沒有用到的部分被填滿。雖然在最低幾級(jí)會(huì)浪費(fèi)一些數(shù)據(jù)位,但總體的收獲還是顯著的。理論上最差的情況是,一個(gè)2K×1的紋理。這里,每一塊只用到一行像素,其余的都沒有用到。

            在單個(gè)紋理內(nèi)的混用不同格式

            需要特別注意的是任何單個(gè)的紋理必須指明它的數(shù)據(jù)——每組16個(gè)texel——是以64位還是以128位存儲(chǔ)的。如果是64位塊——也就是說(shuō),紋理用了DXT1格式,那么在同一紋理內(nèi)以塊為單位,混用不透明和一位阿爾法格式是可以的。換句話說(shuō),對(duì)每個(gè)由16個(gè)texel組成的塊,對(duì)color_00和color_1兩個(gè)無(wú)符號(hào)整數(shù)的比較是單獨(dú)進(jìn)行的。

            一旦使用了128位塊,整個(gè)紋理的阿爾法通道必須被指定為直接模式(DXT2和DXT3格式)或插值模式(DXT4和DXT5格式)。和顏色一樣,一旦選擇了插值模式,就可以以塊為單位,混合使用八位或六位插值阿爾法。對(duì)alpha_0和alpha_1大小的比較仍然是以塊為單位進(jìn)行的。

            對(duì)用于三維建模的紋理,Direct3D提供了壓縮表面的服務(wù)。本節(jié)提供了有關(guān)創(chuàng)建壓縮紋理表面及操控表面中的數(shù)據(jù)的信息。

            信息被分為以下主題。


            不透明和一位阿爾法紋理


            DXT1紋理格式用于不透明的或只有一個(gè)透明色的紋理。

            每個(gè)不透明或一位阿爾法塊保存了兩個(gè)16位顏色值(RGB 5:6:5格式)和一個(gè)4x4的位圖,位圖中的每個(gè)像素占用2位。這樣16個(gè)texel一共占用64位,或每個(gè)像素占用四位。在位圖塊中,每個(gè)texel占用2位,可以選擇四個(gè)顏色,其中兩個(gè)直接存儲(chǔ)在經(jīng)過(guò)編碼的數(shù)據(jù)中,另兩個(gè)則通過(guò)線性插值從存儲(chǔ)的顏色值導(dǎo)出。下圖顯示了這種布局。

             

            可以通過(guò)對(duì)存儲(chǔ)在塊中的兩個(gè)16位顏色值進(jìn)行比較來(lái)區(qū)分一位阿爾法格式和不透明格式。兩個(gè)16位顏色值被當(dāng)作無(wú)符號(hào)整數(shù)。如果第一個(gè)顏色值大于第二個(gè),那么就暗示這一塊只定義了不透明texel。這意味著有四個(gè)顏色可以用來(lái)表示texel。在四色編碼中,有兩個(gè)是導(dǎo)出的顏色,四個(gè)顏色值在RGB顏色空間中均勻分布。這種格式和RGB 5:6:5格式相似。否則(第一個(gè)顏色值小于等于第二個(gè)),就是一位阿爾法格式,一位阿爾法格式可以使用三個(gè)顏色,第四個(gè)顏色被保留,用來(lái)表示透明的texel。

            在三色編碼中,有一個(gè)導(dǎo)出的顏色,第四個(gè)2位編碼被保留,用來(lái)表示透明的texel(阿爾法信息)。這種格式與RGBA 5:5:5:1格式相似,RGBA 5:5:5:1格式的最后一位被用來(lái)編碼阿爾法掩碼。

            以下示例代碼描述了用來(lái)決定當(dāng)前塊是使用了三色編碼還是四色編碼的算法。

            if (color_0 > color_1) 
            {
               // 四色編碼塊:導(dǎo)出另兩個(gè)顏色。
                // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
                // 這些二位編碼對(duì)應(yīng)于存儲(chǔ)在64位塊中的二位位域。
                color_2 = (2 * color_0 + color_1 + 1) / 3;
                color_3 = (color_0 + 2 * color_1 + 1) / 3;
            }    
            else
            { 
                // 三色編碼:導(dǎo)出一個(gè)顏色。
                // 00 = color_0, 01 = color_1, 10 = color_2, 
                // 11 = transparent.
                // 這些二位編碼對(duì)應(yīng)于存儲(chǔ)在64位塊中的二位位域。
                 color_2 = (color_0 + color_1) / 2;    
                 color_3 = transparent;    
             
            }

            應(yīng)用程序在進(jìn)行混合操作之前,最好把透明像素的RGBA成員設(shè)為零。

            下面這些表顯示了八字節(jié)塊的內(nèi)存布局,這里假設(shè)第一個(gè)索引值對(duì)應(yīng)y坐標(biāo),第二個(gè)索引值對(duì)應(yīng)x坐標(biāo)。例如,Texel[1][2]指的是紋理貼圖中位于(x,y) = (2,1)處的像素。

            下表顯示了八字節(jié)(64位)塊的內(nèi)存布局。

            字地址

            16位字

            0

            Color_0

            1

            Color_1

            2

            位圖數(shù)據(jù)Word_0

            3

            位圖數(shù)據(jù)Word_1

            Color_0和Color_1為位于兩端的顏色,它們的布局如下所示。

            顏色

            4:0 (最低位)

            藍(lán)色分量

            10:5

            綠色分量

            15:11

            紅色分量

            位圖數(shù)據(jù)Word_0的布局如下所示。

            Texel

            1:0 (最低位)

            Texel[0][0]

            3:2

            Texel[0][1]

            5:4

            Texel[0][2]

            7:6

            Texel[0][3]

            9:8

            Texel[1][0]

            11:10

            Texel[1][1]

            13:12

            Texel[1][2]

            15:14 (最高位)

            Texel[1][3]

            位圖數(shù)據(jù)Word_1的布局如下所示。

            Texel

            1:0 (最低位)

            Texel[2][0]

            3:2

            Texel[2][1]

            5:4

            Texel[2][2]

            7:6

            Texel[2][3]

            9:8

            Texel[3][0]

            11:10

            Texel[3][1]

            13:12

            Texel[3][2]

            15:14 (最高位)

            Texel[3][3]

            不透明顏色編碼的示例

            作為不透明編碼的一個(gè)例子,假設(shè)位圖左右兩邊的顏色為紅色和黑色。紅色為color_0,黑色為color_1。在它們之間有兩個(gè)插值得到的顏色,四個(gè)顏色一起形成了均勻的顏色變化。以下計(jì)算用來(lái)確定4x4位圖的值。

            00 ? color_0
            01 ? color_1
            10 ? 2/3 color_0 + 1/3 color_1
            11 ? 1/3 color_0 + 2/3 color_1

            位圖看起來(lái)如下圖所示。

             

            這看起來(lái)就是下面的一系列顏色。

             

            一位阿爾法編碼的示例

            當(dāng)16位無(wú)符號(hào)整數(shù)color_0小于color_1時(shí),就表示選擇一位阿爾法格式。舉個(gè)例子,這種格式可以用來(lái)顯示在藍(lán)天背景前的樹葉,可以把一些texel標(biāo)記成透明的,而樹葉還可以使用三種深度的綠色。其中兩個(gè)顏色位于兩端,第三個(gè)顏色通過(guò)插值得到。

            這里顯示了這樣一幅圖片。

             

            注意圖像中白色的地方,texel會(huì)被編碼成透明的。此外還要注意在進(jìn)行混合前,應(yīng)該把透明texel的RGBA分量設(shè)為零。

            以下計(jì)算用來(lái)確定位圖的顏色和透明度的編碼。

            00 ? color_0
            01 ? color_1
            10 ? 1/2 color_0 + 1/2 color_1
            11   ?   Transparent

            位圖看起來(lái)如下圖所示。

             


            帶阿爾法通道的紋理


            可以用有兩種方法對(duì)具有更為復(fù)雜的透明度的紋理貼圖進(jìn)行編碼。每種方法都包含一個(gè)描述透明度的塊,位于前述的64位塊之前。透明度要么用4x4位圖表示,位圖中每像素占用4位(直接編碼),要么占用更少的位(譯注:3位)并進(jìn)行線性插值,這與顏色編碼相似。

            透明度塊和顏色塊中數(shù)據(jù)的排列如下表所示。

            字地址

            64位塊

            3:0

            透明度塊

            7:4

            前述64位塊

            直接紋理編碼

            對(duì)于直接紋理編碼(DXT2和DXT3格式),描述texel的透明度的阿爾法分量被編碼在一個(gè)4x4位圖中,每個(gè)texel占用4位。這四位阿爾法值可以通過(guò)各種方法得到,諸如抖動(dòng)或使用阿爾法數(shù)據(jù)的最高四位。但無(wú)論它們是如何產(chǎn)生的,都只按原樣使用,不做任何形式的插值。

            下圖顯示了一個(gè)64位的透明度塊。

             

            注意    Microsoft® Direct3D®的壓縮方法使用最高四位。

            下面這些表描述了在每個(gè)16位字中,阿爾法信息的內(nèi)存布局。

            下表包含了字0的布局。

            阿爾法

            3:0 (最低位)

            [0][0]

            7:4

            [0][1]

            11:8

            [0][2]

            15:12 (最高位)

            [0][3]

            下表包含了字1的布局。

            阿爾法

            3:0 (最低位)

            [1][0]

            7:4

            [1][1]

            11:8

            [1][2]

            15:12 (最高位)

            [1][3]

            下表包含了字2的布局。

            阿爾法

            3:0 (最低位)

            [2][0]

            7:4

            [2][1]

            11:8

            [2][2]

            15:12 (最高位)

            [2][3]

            下表包含了字3的布局。

            阿爾法

            3:0 (最低位)

            [3][0]

            7:4

            [3][1]

            11:8

            [3][2]

            15:12 (最高位)

            [3][3]

            DXT2和DXT3之間的區(qū)別在于,在DXT2格式中,我們認(rèn)為顏色數(shù)據(jù)已經(jīng)預(yù)乘了阿爾法,而在DXT3格式中,我們認(rèn)為顏色數(shù)據(jù)沒有預(yù)乘阿爾法。之所以需要這兩種是格式是因?yàn)榇蠖鄶?shù)情況下,在使用一個(gè)紋理時(shí),僅檢查數(shù)據(jù)并不足以確定顏色數(shù)據(jù)是否已經(jīng)預(yù)乘了阿爾法。因?yàn)檫\(yùn)行的時(shí)候需要這樣的信息,所以就用兩個(gè)四字符碼(FOURCC)來(lái)區(qū)分這兩種情況。但是,這兩種格式使用的數(shù)據(jù)和插值方法是相同的。

            為了檢測(cè)texel是否是透明的,在DXT1中要對(duì)顏色進(jìn)行比較,而DXT2和DXT3格式?jīng)]有使用種比較。我們認(rèn)為在不需要顏色比較的情況下,顏色數(shù)據(jù)總是以四色模式進(jìn)行處理。換句話說(shuō),DXT1代碼中最上面的if語(yǔ)句應(yīng)該是:

            if ((color_0 > color_1) OR !DXT1) {

            三位線性阿爾法插值

            對(duì)DXT4和DXT5格式的透明度的編碼基于線性插值,這和顏色編碼中使用的線性編碼相似。這兩種編碼方法中,第一個(gè)八字節(jié)塊存儲(chǔ)了兩個(gè)8位阿爾法值和一個(gè)4x4位圖,位圖中的每個(gè)像素占用三位。存儲(chǔ)的兩個(gè)阿爾法值用來(lái)插值導(dǎo)出中間的阿爾法值。其它信息根據(jù)兩個(gè)阿爾法值的存儲(chǔ)方式有所不同。如果alpha_0大于alpha_1,那么插值會(huì)導(dǎo)出六個(gè)中間的阿爾法值。反之,插值會(huì)導(dǎo)出四個(gè)中間的阿爾法值,另外兩個(gè)隱含的阿爾法值分別為0(完全透明)和255(完全不透明)。

            以下示例代碼描述了這種算法。

            // 含8個(gè)阿爾法還是含6個(gè)阿爾法的塊? 
            if (alpha_0 > alpha_1) {    
                // 含八個(gè)阿爾法的塊:導(dǎo)出另外六個(gè)阿爾法值。 
                // 位編碼000 = alpha_0, 001 = alpha_1, 其它值通過(guò)插值得到。
                alpha_2 = (6 * alpha_0 + 1 * alpha_1 + 3) / 7;    // 位編碼010
                alpha_3 = (5 * alpha_0 + 2 * alpha_1 + 3) / 7;    // 位編碼011
                alpha_4 = (4 * alpha_0 + 3 * alpha_1 + 3) / 7;    // 位編碼100
                alpha_5 = (3 * alpha_0 + 4 * alpha_1 + 3) / 7;    // 位編碼101
                alpha_6 = (2 * alpha_0 + 5 * alpha_1 + 3) / 7;    // 位編碼110
                alpha_7 = (1 * alpha_0 + 6 * alpha_1 + 3) / 7;    // 位編碼111 
            }    
            else
                // 含六個(gè)阿爾法的塊。 
                // 位編碼000 = alpha_0, 001 = alpha_1, 其它值通過(guò)插值得到。
                alpha_2 = (4 * alpha_0 + 1 * alpha_1 + 2) / 5;    // 位編碼010
                alpha_3 = (3 * alpha_0 + 2 * alpha_1 + 2) / 5;    // 位編碼011
                alpha_4 = (2 * alpha_0 + 3 * alpha_1 + 2) / 5;    // 位編碼100
                alpha_5 = (1 * alpha_0 + 4 * alpha_1 + 2) / 5;    // 位編碼101
                alpha_6 = 0;                                      // 位編碼110
                alpha_7 = 255;                                    // 位編碼111
            }

            阿爾法塊的內(nèi)存布局如下所示:

            字節(jié)

            阿爾法

            0

            Alpha_0

            1

            Alpha_1

            2

            [0][2] (最低2), [0][1], [0][0]

            3

            [1][1] (最低1), [1][0], [0][3], [0][2] (最高1)

            4

            [1][3], [1][2], [1][1] (最高2)

            5

            [2][2] (最低2), [2][1], [2][0]

            6

            [3][1] (最低1), [3][0], [2][3], [2][2] (最低1)

            7

            [3][3], [3][2], [3][1] (最高2)

            DXT4和DXT5之間的區(qū)別在于,在DXT4格式中,我們認(rèn)為顏色數(shù)據(jù)已經(jīng)預(yù)乘了阿爾法,而在DXT5格式中,我們認(rèn)為顏色數(shù)據(jù)沒有預(yù)乘阿爾法。之所以需要這兩種是格式是因?yàn)榇蠖鄶?shù)情況下,在使用一個(gè)紋理時(shí),僅檢查數(shù)據(jù)不足以確定顏色數(shù)據(jù)是否已經(jīng)預(yù)乘了阿爾法。因?yàn)檫\(yùn)行的時(shí)候需要這樣的信息,所以就用兩個(gè)四字符碼(FOURCC)來(lái)區(qū)分這兩種情況。但是,這兩種格式使用的數(shù)據(jù)和插值方法是相同的。

            為了檢測(cè)texel是否是透明的,在DXT1中要對(duì)顏色進(jìn)行比較,而DXT4和DXT5格式?jīng)]有使用這種比較。我們認(rèn)為在不需要顏色比較的情況下,顏色數(shù)據(jù)總是以四色模式進(jìn)行處理。換句話說(shuō),DXT1代碼中最上面的if語(yǔ)句應(yīng)該是:

            if ((color_0 > color_1) OR !DXT1) {

            壓縮紋理格式


            本節(jié)包含了有關(guān)壓縮紋理格式的內(nèi)部結(jié)構(gòu)的信息。應(yīng)用程序開發(fā)人員并不需要了解這些細(xì)節(jié)以使用壓縮紋理,因?yàn)閼?yīng)用程序可以使用Direct3D擴(kuò)展(D3DX)函數(shù)在壓縮和未壓縮紋理間進(jìn)行轉(zhuǎn)換。但是,如果應(yīng)用程序需要直接對(duì)壓縮表面中的數(shù)據(jù)進(jìn)行操作,那么這些信息就很有用。

            Microsoft® Direct3D®使用的壓縮格式把紋理貼圖分成4x4的texel塊。如果紋理不包含透明度——也就是說(shuō)是不透明的——或者透明度用一位阿爾法表示,那么就用一個(gè)8字節(jié)的塊表示紋理貼圖塊。如果紋理貼圖確實(shí)在阿爾法通道中包含了透明度信息,那么就用一個(gè)16字節(jié)的塊表示紋理貼圖塊。

            • 不透明和一位阿爾法紋理
            • 帶阿爾法通道的紋理

            注意    任何單個(gè)紋理必須指定它的數(shù)據(jù)——每組16個(gè)texel——是以64位還是以128位存儲(chǔ)的。如果紋理用了64位塊——也就是DXT1格式,那么可以以塊為單位在同一張紋理中混合使用不透明和一位阿爾法格式。換句話說(shuō),對(duì)每個(gè)由16個(gè)texel組成的塊來(lái)說(shuō),對(duì)無(wú)符號(hào)整數(shù)color_0color_1的比較是單獨(dú)進(jìn)行的。

            在使用128位的塊時(shí),必須給整個(gè)紋理的阿爾法通道指定直接模式(DXT2或DXT3)或插值模式(DXT4或DXT5)。和顏色一樣,當(dāng)選擇了插值模式時(shí),可以以每一塊為單位混合使用八阿爾法模式或六阿爾法模式。對(duì)alpha_0和alpha_1大小的比較仍然是以塊為單位進(jìn)行的。

            當(dāng)前DXTn格式的pitch與Microsoft DirectX® 7.0中返回的不同。該值現(xiàn)在指的是以塊為單位的每一行的pitch。例如,如果應(yīng)用程序有一個(gè)寬度為16的壓縮紋理,那么pitch值會(huì)是四塊(DXT1為4*8,DXT2-5為4*16)。


            使用壓縮紋理


            測(cè)定對(duì)壓縮紋理的支持

            在應(yīng)用程序創(chuàng)建一個(gè)渲染設(shè)備前,可以通過(guò)調(diào)用IDirect3D9::CheckDeviceFormat方法測(cè)定設(shè)備是否支持用壓縮紋理進(jìn)行紋理操作。該方法測(cè)定是否可以在設(shè)備上使用某種表面格式。

            要測(cè)試適配器,應(yīng)該把像素格式指定為DXT1, DXT2, DXT3, DXT4, 或DXT5四字符碼。如果IDirect3D9::CheckDeviceFormat返回D3D_OK,那么設(shè)備可以從該格式的壓縮紋理直接創(chuàng)建紋理。如果是這樣,那么應(yīng)用程序就可以通過(guò)調(diào)用IDirect3DDevice9::SetTexture方法,在Microsoft® Direct3D®中直接使用壓縮紋理表面。以下示例代碼顯示了如何測(cè)定適配器是否支持壓縮紋理格式。

            BOOL IsCompressedTextureFormatOk( D3DFORMAT TextureFormat, 
                                              D3DFORMAT AdapterFormat ) {
                HRESULT hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT,
                                                      D3DDEVTYPE_HAL,
                                                      AdapterFormat,
                                                      0,
                                                      D3DRTYPE_TEXTURE,
                                                      TextureFormat);
             
                return SUCCEEDED( hr );
            }

            如果設(shè)備不支持用壓縮紋理表面進(jìn)行紋理操作,那么應(yīng)用程序仍然可以在壓縮格式的表面中存儲(chǔ)數(shù)據(jù),但是必須在使用紋理之前,把任何壓縮紋理轉(zhuǎn)換為設(shè)備支持的格式。

            創(chuàng)建壓縮紋理

            在創(chuàng)建了一個(gè)支持壓縮紋理格式的設(shè)備后,應(yīng)用程序就可以創(chuàng)建壓縮紋理資源了。只要調(diào)用IDirect3DDevice9::CreateTexture并在Format參數(shù)中指定一個(gè)壓縮紋理格式即可。

            在把圖像載入紋理對(duì)象之前,應(yīng)該調(diào)用IDirect3DTexture9::GetSurfaceLevel方法取得一個(gè)指向紋理表面的指針。

            現(xiàn)在應(yīng)用程序就可以調(diào)用任何D3DXLoadSurfacexxx函數(shù),把圖像載入先前用IDirect3DTexture9::GetSurfaceLevel取得的表面。

            DirectX®軟件開發(fā)包(SDK)提供的DXTex工具,開發(fā)人員可以創(chuàng)建壓縮紋理(DDS)文件并在不同的格式間進(jìn)行轉(zhuǎn)換。

            這種方法的好處在于應(yīng)用程序可以把壓縮表面的內(nèi)容存到文件中,而不必根據(jù)表面的格式以及紋理的寬度和高度計(jì)算所需的存儲(chǔ)容量。

            下表顯示了五種壓縮紋理類型。更多有關(guān)數(shù)據(jù)存儲(chǔ)方式的信息,請(qǐng)參閱壓縮紋理格式。開發(fā)人員只有在要寫自己的壓縮函數(shù)時(shí)才需要了解這些信息。

            四字符碼

            描述

            預(yù)乘阿爾法?

            DXT1

            不透明/一位阿爾法

            N/A

            DXT2

            直接阿爾法

            DXT3

            直接阿爾法

            DXT4

            插值阿爾法

            DXT5

            插值阿爾法

             

            注意 當(dāng)應(yīng)用程序要把數(shù)據(jù)從未預(yù)乘阿爾法格式傳輸?shù)揭杨A(yù)乘阿爾法格式時(shí),Direct3D會(huì)根據(jù)阿爾法值縮放顏色值。Direct3D不支持把數(shù)據(jù)從已預(yù)乘阿爾法格式傳輸?shù)轿搭A(yù)乘阿爾法格式。如果應(yīng)用程序試圖把數(shù)據(jù)從已預(yù)乘阿爾法的源數(shù)據(jù)傳輸?shù)轿搭A(yù)乘阿爾法的目標(biāo)數(shù)據(jù)時(shí),被調(diào)用的方法會(huì)返回D3DERR_INVALIDCALL。如果應(yīng)用程序要把數(shù)據(jù)從已預(yù)乘阿爾法的源數(shù)據(jù)傳輸?shù)讲缓柗ǖ哪繕?biāo)數(shù)據(jù)時(shí),那么源數(shù)據(jù)中已預(yù)乘過(guò)阿爾法的顏色分量會(huì)被直接復(fù)制,不做任何改變。

            壓縮紋理的解壓

            正如對(duì)紋理表面的壓縮,對(duì)壓縮紋理的解壓也是通過(guò)Direct3D的復(fù)制服務(wù)進(jìn)行的。

            要把壓縮紋理復(fù)制到未壓縮的紋理表面,應(yīng)該使用函數(shù)D3DXLoadSurfaceFromSurface。這個(gè)函數(shù)會(huì)處理對(duì)表面的壓縮和解壓。


            使用紋理時(shí)需要考慮的硬件問題


            當(dāng)前的硬件并不一定支持Microsoft® Direct3D®接口提供的全部功能。應(yīng)用程序必須測(cè)試用戶的硬件并相應(yīng)地調(diào)整渲染策略。

            許多三維加速卡不支持把經(jīng)過(guò)迭代的漫反射色作為混合單元的參數(shù),但是,應(yīng)用程序可以在進(jìn)行紋理混合時(shí)使用經(jīng)過(guò)迭代的顏色數(shù)據(jù)。

            一些三維硬件的第一層紋理可能沒有相應(yīng)的紋理混合層,在此類適配器上,應(yīng)用程序應(yīng)該在當(dāng)前紋理集合的第二和第三紋理層進(jìn)行混合操作。

            因?yàn)楫?dāng)前大多數(shù)硬件的限制,很少有適配器可以通過(guò)IDirect3DDevice9提供的多重紋理混合接口執(zhí)行三線性mipmap插值。應(yīng)用程序可以用多趟紋理混合達(dá)到相同的效果,或者降級(jí)使用被廣為支持的D3DTEXF_POINT mipmap過(guò)濾模式。


            立體紋理資源


            立體紋理是三維像素(texel)的集合,可以用來(lái)繪制諸如三角形或線之類的二維圖元。對(duì)于每個(gè)使用立體紋理的圖元來(lái)說(shuō),圖元的每個(gè)頂點(diǎn)需要包含三元素紋理坐標(biāo)。在繪制圖元時(shí),圖元覆蓋的每個(gè)像素的顏色來(lái)自立體紋理中的某個(gè)像素,這和二維紋理中的情況相一致。因?yàn)椴淮嬖诳梢杂昧Ⅲw紋理進(jìn)行繪制的真正的三維圖元,所以立體紋理不能直接用來(lái)渲染。

            應(yīng)用程序可以把立體紋理用于諸如patchy fog,爆炸等特效。

            立體紋理由多片組織而成,可以把它想象成把大小為width x height的二維表面疊在一起形成的大小為width x height x depth立體紋理。每片為一行。立體紋理可以有后續(xù)的級(jí),每一級(jí)的大小為前一級(jí)大小的一半。下圖給出了一個(gè)多級(jí)立體紋理看起來(lái)的樣子。

             

            創(chuàng)建立體紋理

            以下示例代碼顯示了使用立體紋理所需的步驟。

            首先,定義一個(gè)包含三個(gè)紋理坐標(biāo)的自定義頂點(diǎn)類型,如以下示例代碼所示。

            struct VOLUMEVERTEX

            {

                FLOAT x, y, z;

                DWORD color;

                FLOAT tu, tv, tw;

            };

             

            #define D3DFVF_VOLUMEVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|

                                         D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE3(0))

            然后,把數(shù)據(jù)填入頂點(diǎn)。

            VOLUMEVERTEX g_vVertices[4] =

            {

                { 1.0f, 1.0f, 0.0f, 0xffffffff, 1.0f, 1.0f, 0.0f },

                {-1.0f, 1.0f, 0.0f, 0xffffffff, 0.0f, 1.0f, 0.0f },

                { 1.0f,-1.0f, 0.0f, 0xffffffff, 1.0f, 0.0f, 0.0f },

                {-1.0f,-1.0f, 0.0f, 0xffffffff, 0.0f, 0.0f, 0.0f }

            };

            現(xiàn)在,創(chuàng)建一個(gè)頂點(diǎn)緩存,并填入頂點(diǎn)數(shù)據(jù)。

            下一步是用IDirect3DDevice9::CreateVolumeTexture創(chuàng)建一個(gè)立體紋理,如以下示例代碼所示。

            LPDIRECT3DVOLUMETEXTURE9 pVolumeTexture;

            d3dDevice->CreateVolumeTexture( 8, 4, 4, 1, 0, D3DFMT_R8G8B8,D3DPOOL_MANAGED,

                                            &pVolumeTexture );

            在渲染圖元之前,把當(dāng)前紋理設(shè)為前面創(chuàng)建的立體紋理。以下示例代碼顯示了渲染一個(gè)三角形帶的整個(gè)過(guò)程。

            if( SUCCEEDED( d3dDevice->BeginScene() ) )

            {

                // 用立體紋理繪制四邊形。

                d3dDevice->SetTexture( 0, pVolumeTexture );

                d3dDevice->SetFVF( D3DFVF_VOLUMEVERTEX );

                d3dDevice->SetStreamSource( 0, pVB, sizeof(VOLUMEVERTEX) );

                d3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2);

             

               // 結(jié)束繪制。

               d3dDevice->EndScene();

            }

            posted on 2009-03-24 08:23 狂爛球 閱讀(16118) 評(píng)論(1)  編輯 收藏 引用 所屬分類: 圖形編程

            評(píng)論

            # re: 紋理(講得比較詳細(xì)的文章) 2009-08-09 17:52 小肥肉

            很強(qiáng)大 這么詳細(xì)  回復(fù)  更多評(píng)論   

            久久精品国产半推半就| 欧美激情精品久久久久| 麻豆av久久av盛宴av| 人妻精品久久无码区| 国产精品欧美久久久久天天影视| 久久久人妻精品无码一区| 久久亚洲国产精品123区| 久久人人爽人人爽人人片AV不| 91精品免费久久久久久久久| 欧美黑人激情性久久| 91久久精品电影| 久久久久亚洲av无码专区导航| 久久99精品免费一区二区| AV色综合久久天堂AV色综合在 | 91精品国产综合久久香蕉 | 精品国产青草久久久久福利| 色综合久久无码中文字幕| 久久精品国产一区二区| 久久电影网2021| 久久精品午夜一区二区福利| 国产精品99久久久久久宅男小说| 国产成人无码精品久久久免费| 久久ZYZ资源站无码中文动漫| 久久久久人妻一区二区三区| 欧美激情精品久久久久久| 欧美久久综合性欧美| 国产91色综合久久免费分享| 午夜不卡久久精品无码免费| 精品久久久久久中文字幕大豆网| 一级女性全黄久久生活片免费 | 国产午夜精品久久久久九九| 久久99国产精品久久99| 久久久久亚洲精品天堂| 久久精品国产久精国产思思 | 亚洲午夜久久久久久噜噜噜| 漂亮人妻被中出中文字幕久久| 一本大道久久香蕉成人网| 久久国产免费直播| 久久综合亚洲欧美成人| 99久久久国产精品免费无卡顿| 久久久久亚洲精品天堂|