天天看點

紋理(講得比較詳細的文章)紋理尋址模式用Mipmap進行紋理過濾複制到表面複制表面Gamma控制壓縮紋理資源不透明和一位阿爾法紋理帶阿爾法通道的紋理壓縮紋理格式使用壓縮紋理

紋理是增強計算機生成的三維圖像的真實感的有力工具。Microsoft® Direct3D®支援廣泛的紋理特性,并使開發人員可以很友善地使用進階紋理技術。

本節講述如何使用紋理。

  • 紋理的基本概念
  • 紋理坐标
  • 紋理過濾
  • 紋理資源
  • 紋理環繞
  • 紋理混合
  • 表面

以下主題将更詳細地介紹另外的紋理功能。

  • Mipmap的自動生成
  • 自動紋理管理
  • 壓縮紋理資源
  • 使用紋理時需要考慮的硬體問題
  • 立體紋理資源

要提高性能,可以考慮使用動态紋理。動态紋理在每一幀都可以被鎖定,寫入及解鎖。更多資訊請參閱使用動态紋理。

紋理的基本概念

早期計算機生成的三維圖像看起來往往像是發亮的塑膠,雖然這在當時也是比較先進的,但是它們缺乏各種紋路——如磨損、裂痕、指紋和污漬等,而這些紋路會增加三維物體的真實感。近年來,紋理已經在開發人員中得到普及并作為增強計算機生成的三維圖像的真實感的工具。

詞語“紋理”在日常使用中表示物體的光滑度或粗糙度,但是在計算機圖形學中,紋理指的是一張表示物體表面細節的位圖。

因為Direct3D中所有紋理都是位圖,是以可以把任何位圖貼到Direct3D圖元的表面。例如,應用程式可以建立物體并使它們的表面看起來有木紋的樣式。可以把草、泥土和岩石等紋理貼在構成山的圖元的表面,這樣就能得到看起來很真實的山坡。應用程式也可以用紋理建立其它的效果,如:路邊的路标,懸崖邊的岩層,或是地面上的大理石。

另外,Direct3D支援更進階的紋理技術,如紋理混合(包含或不含透明度)和光照貼圖。更多資訊請參閱紋理混合和用紋理實作光照貼圖。

如果應用程式建立一個HAL裝置或軟體裝置,那麼可以使用8、16、24或是32位紋理。

以下主題包含了更多的資訊。

  • 紋理尋址模式
  • 無效紋理區域
  • 紋理調色闆

紋理尋址模式

Microsoft® Direct3D®應用程式可以把紋理坐标值賦給任何圖元的任何頂點。更多細節,請參閱紋理坐标。一般來說,應用程式賦給頂點的u、v紋理坐标值在0.0到1.0範圍内,閉區間。但是,通過把紋理坐标值賦為此範圍外的值,應用程式可以建立某些特殊紋理效果。

通過設定紋理尋址模式,應用程式可以控制當紋理坐标位于範圍[0.0, 1.0]外時希望Direct3D執行何種操作。例如,應用程式可以設定尋址模式,使紋理平鋪于圖元表面。下面的主題包含了更多的細節。

Direct3D使應用程式可以進行紋理環繞,很重要的一點是要注意把紋理尋址模式設為D3DTADDRESS_WRAP與進行紋理環繞并不相同。把紋理尋址模式設為D3DTADDRESS_WRAP會使源紋理的多個複本被貼到目前圖元的表面,而啟用紋理環繞則會改變系統對貼有紋理的多邊形進行光栅化的方式。更多細節,請參閱紋理環繞。

啟用紋理環繞實際上使位于[0.0, 1.0]範圍之外的紋理坐标無效,在這種情況下,對無效的紋理坐标進行光栅化操作将導緻未定義的結果。當啟用紋理環繞時,不會使用紋理尋址模式,同時應用程式應該注意不要給出小于0.0或大于1.0的紋理坐标。

設定尋址模式

應用程式可以通過調用IDirect3DDevice9::SetSamplerState方法設定每個紋理層的紋理尋址模式,隻需把紋理層的辨別作為第一個參數,并把第二個參數設定為D3DSAMP_ADDRESSU,D3DSAMP_ADDRESSV或D3DSAMP_ADDRESSW,就可以分别改變u,v或w尋址模式。IDirect3DDevice9::SetSamplerState方法的第三個參數決定要設定的模式,這是一個D3DTEXTUREADDRESS枚舉類型值。要取得某一紋理層目前的紋理尋址模式,隻需調用IDirect3DDevice9::GetSamplerState方法,把第二個參數設定為D3DTEXTURESTAGESTATETYPE枚舉類型值,并把第三個參數設定為用于儲存傳回的尋址模式的變量的位址。

裝置限制

雖然系統允許紋理坐标位于閉區間0.0到1.0之外,但硬體限制通常會決定紋理坐标究竟可以超出該範圍多少。當應用程式取得裝置能力時,渲染裝置通過D3DCAPS9結構的MaxTextureRepeat成員表明這個限制,這個成員的值描述了裝置允許的紋理坐标的全部範圍。例如,若這個值為128,則輸入的紋理坐标必須保持在從-128.0到+128.0之間,若輸入頂點的紋理坐标位于這個範圍外,則紋理坐标是無效的。對自動生成的紋理坐标和由紋理坐标變換得到的紋理坐标也有同樣的限制。

對MaxTextureRepeat的解釋同時還受D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE能力位的影響。若設定了這個能力位,則D3DCAPS9結構的MaxTextureRepeat成員的值正如前面描述的一樣。但是,若沒有設定該能力位,則紋理循環的限制還取決于紋理坐标尋址的那個紋理的大小。在這種情況下,MaxTextureRepeat必須除以目前mipmap中最大紋理的大小。例如,若紋理大小為32,而MaxTextureRepeat的值為512,則實際有效的紋理坐标的範圍是512/32 = 16,是以傳給該裝置的紋理坐标必須在範圍-16.0到+16.0之間。

以下主題包含了更多有關紋理尋址的資訊:        

  • 環繞紋理尋址模式
  • 鏡像紋理尋址模式
  • 截取紋理尋址模式
  • 邊框顔色紋理尋址模式

環繞紋理尋址模式

環繞紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_WRAP成員表示,它會使Microsoft® Direct3D®在紋理坐标的整數邊界重複使用該紋理。例如,設想應用程式建立了一個方的圖元并把紋理坐标指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設定為D3DTADDRESS_WRAP會使紋理在u和v方向都重複三次。

這種紋理尋址模式的效果與鏡像紋理尋址模式有些相似,卻又明顯不同。更多資訊,請參閱鏡像紋理尋址模式。

鏡像紋理尋址模式

鏡像紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_MIRROR成員表示,它會使Microsoft® Direct3D®在紋理坐标的整數邊界先對紋理進行鏡像然後再重複使用。例如,設想應用程式建立了一個方的圖元并把紋理坐标指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設定為D3DTADDRESS_MIRROR會使紋理在u和v方向都重複三次,每一行和每一列的紋理都是相鄰行和列的紋理的鏡像。

這種紋理尋址模式的效果與環繞紋理尋址模式有些相似,卻又明顯不同。更多資訊,請參閱環繞紋理尋址模式。

截取紋理尋址模式

截取紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_CLAMP成員表示,它會使Microsoft® Direct3D®把紋理坐标截取到[0.0, 1.0]範圍内,也就是說,這種模式隻應用紋理一次,然後就重複使用紋理邊緣處像素的顔色。例如,設想應用程式建立了一個方的圖元并把紋理坐标指定為(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把紋理尋址模式設定為D3DTADDRESS_CLAMP會使紋理隻被應用一次,列的頂端和行的末端處像素的顔色被相應地延伸至圖元的頂端和右邊。

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

邊框顔色紋理尋址模式

邊框顔色紋理尋址模式由D3DTEXTUREADDRESS枚舉類型的D3DTADDRESS_BORDER成員表示,該尋址模式會使Microsoft® Direct3D®對于位于[0.0, 1.0]範圍之外的紋理坐标使用一個被稱為邊框顔色的指定顔色。

下圖描繪了邊框顔色紋理尋址模式,這裡應用程式指定紅色為紋理的邊框顔色。

應用程式可以通過調用IDirect3DDevice9::SetSamplerState方法設定邊框顔色。在調用時要把第一個參數設為想要設定的紋理層的辨別,把第二個參數設為D3DSAMP_BORDERCOLOR紋理層狀态值,并把第三個參數設為以RGBA形式表示的新的邊框顔色。

無效紋理區域

通過給紋理指定無效區域,應用程式可以對需要複制紋理的哪些子集進行優化,隻有那些被标記為無效的區域才會被IDirect3DDevice9::UpdateTexture方法更新。當建立紋理時,整個紋理被标記為無效的。隻有以下幾種操作可以改變紋理的無效狀态。

  • 給一個紋理添加一個無效區域。
  • 鎖定紋理中的一些區域。此操作會把被鎖定的區域添加到無效區域中,如果應用程式明确知道哪些是真正的無效區域,那麼也可以關閉對無效區域的自動更新。
  • 将紋理作為目标表面進行更新的話會把整個紋理标記為無效的。
  • 對紋理調用IDirect3DDevice9::UpdateTexture方法會清除該紋理的所有無效區域。
  • 為了得到裝置上下文(device context)而調用IDirect3DDevice9::GetDC。

對于mipmap紋理而言,無效區域被設在最高一級的紋理上,為了最小化對mipmap紋理中每一級的紋理更新所需複制的位元組數,IDirect3DDevice9::UpdateTexture方法可以擴充無效區域并沿mipmap鍊更新子紋理。注意子級中無效區域的紋理坐标被向上舍入,也就是說,它們的小數部分被向上取整到紋理中最近的像素。

因為每種類型的紋理具有不同類型的無效區域,是以每種類型的紋理都有相應的方法表示無效區域。二維紋理使用矩形,立體紋理使用立方體。

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

把以上方法的pDirtyRect或pDirtyBox參數設定為NULL會擴大無效區域并使之覆寫整個紋理。

每種鎖定方法都有D3DLOCK_NO_DIRTY_UPDATE标志,使用這個标志可以防止對紋理無效區域的改變。更多資訊,請參閱鎖定資源。

如果在鎖定操作時可以得到已改變區域的完整集合,那麼應用程式應該使用D3DLOCK_NO_DIRTY_UPDATE标志。注意,對紋理一個的子級的鎖定或複制操作(也就是說,未對紋理的最高一級進行鎖定或複制操作)不會更新該紋理的無效區域。當應用程式鎖定了紋理的子級而沒有鎖定紋理的最高一級時,它同樣有責任對無效區域進行更新。

紋理調色闆

Microsoft DirectX® 9.0中的Microsoft® Direct3D®通過一組與IDirect3DDevice9對象相關聯的256色調色闆支援調色闆紋理(paletted texture)。通過調用IDirect3DDevice9::SetCurrentTexturePalette方法可以設定目前調色闆。目前調色闆用于對所有已激活的紋理層中的所有調色闆紋理進行顔色轉換。IDirect3DDevice9::SetPaletteEntries方法可以更新調色闆中的全部256個顔色項。每個顔色項都是一個用D3DFMT_A8R8G8B8格式表示的PALETTEENTRY結構,預設值為0xFFFFFFFF。

IDirect3DDevice9的調色闆包含了一個阿爾法通道。若裝置設定了D3DPTEXTURECAPS_ALPHAPALETTE能力位,則表示該裝置支援調色闆阿爾法,并可以使用該阿爾法通道。當紋理格式不含阿爾法通道時,就使用調色闆阿爾法通道。若裝置不支援調色闆阿爾法,同時紋理格式也不含阿爾法通道,則使用0xFF作為阿爾法值。

系統中最多可以有65,536個調色闆。因為調色闆占用的記憶體資源與應用程式引用到的最大的調色闆編号成正比,是以最好使用從零開始且連續的編号。

紋理坐标

大多數紋理,如位圖,都是一個存放顔色值的二維數組,但立方體環境貼圖除外,具體細節請參閱立方體環境貼圖。數組中的每個顔色值被稱為texel。每個texel在紋理中有唯一的位址,可以認為這個位址是行和列的編号,它們分别被标記為u和v。

紋理坐标位于紋理空間中,也就是說,它們相對于紋理中的位置(0,0)點。當把紋理貼到三維空間中圖元的表面時,紋理的texel必須先被映射到對象坐标系,然後再變換到螢幕坐标系,或像素的位置。

将Texel映射到螢幕空間

Microsoft® Direct3D®直接把紋理中的texel映射到螢幕空間,這樣就省略了中間步驟并極大地提高了效率。這個映射的過程實際上是一個反向映射,也就是說,系統根據每個像素在螢幕空間中的位置計算該像素在紋理空間中相應的texel的位置,然後對位于該點或該點附近的紋理顔色進行取樣。取樣的過程被稱為紋理過濾。更多資訊請參閱紋理過濾。

紋理中每個texel的位置可以用它的texel坐标表示。但是為了把texel貼到圖元表面,Direct3D需要所有的紋理中的texel具有相同的位址範圍,是以Direct3D使用了一種通用的尋址方法。在這種尋址方法中,所有texel的位址都在閉區間0.0到1.0内。Direct3D用u,v值表示紋理坐标,這和用x,y坐标表示二維笛卡爾坐标系非常相似。從技術上講,系統事實上可以處理0.0到1.0範圍外的紋理坐标,系統根據應用程式設定的紋理尋址模式來進行此類處理。更多資訊,請參閱紋理尋址模式。

采用這種方法的結果是相同的紋理位址在不同的紋理中會映射到不同的texel坐标。在下圖中,正在使用的紋理位址是(0.5,1.0)。但是,因為紋理的大小不同,是以該紋理位址映射到不同的texel。左邊紋理的大小為5x5,紋理位址(0.5,1.0)映射到texel (2,4)。右邊紋理的大小為7x7,紋理位址(0.5,1.0)映射到texel (3,6)。

下圖描述了一個經簡化的texel映射過程。這個示例顯然非常簡單,要了解更多細節資訊,請參閱直接把Texel映射到像素。

本例中,圖的左邊顯示了一個被理想化為正方形色塊的像素。像素四角的位址被映射到對象空間中的三維圖元,因為三維場景中圖元的形狀及觀察角度的不同,像素的形狀常常會被扭曲。像素四角所對應圖元表面的區域然後被映射到紋理空間,這個映射過程會再次扭曲像素的形狀。像素的最終顔色根據像素映射的區域所覆寫的texel計算得到。通過設定紋理過濾方法,應用程式可以控制讓Direct3D使用何種方法計算像素顔色。更多資訊,請參閱紋理過濾。

應用程式可以直接給頂點指定紋理坐标,這使應用程式可以控制把紋理的哪些部分貼到圖元上。例如,設想應用程式建立了一個與下面的紋理大小完全相同的圖元,本例中,如果應用程式希望把整張紋理貼到整面牆上,那麼應用程式給圖元的頂點指定的紋理坐标就應該是(0.0,0.0), (1.0,0.0), (1.0,1.0), 和 (0.0,1.0)。

如果應用程式決定把牆的高度減半,應用程式仍可以把整張貼圖貼到稍小的牆上,但這會擠壓紋理并使之扭曲,或者應用程式也可以重新指定紋理坐标,使Direct3D使用紋理的下半部分。

如果應用程式為了把紋理貼到稍小的牆上而決定擠壓或拉伸紋理,那麼應用程式使用的紋理過濾方法會影響最終圖像的品質。更多資訊,請參閱紋理過濾。

如果應用程式決定重新指定紋理坐标并使Direct3D使用紋理的下半部分,那麼在本例中應用程式給圖元的頂點指定的紋理坐标應該是(0.0,0.5), (1.0,0.5), (1.0,1.0), 和 (0.0,1.0),這樣Direct3D就會把紋理的下半部分貼到牆上。

頂點的紋理坐标有可能大于1.0,如果應用程式給頂點指定的紋理坐标不在閉區間0.0到1.0範圍内,那麼應用程式還應該設定紋理尋址模式。更多資訊,請參閱紋理尋址模式。

紋理坐标和紋理層

紋理坐标通過紋理層與紋理聯系在一起。紋理通過SetTexture(stageIndex, pTextre) 被設定到某一紋理層。請參閱IDirect3DDevice9::SetTexture。

一個彈性頂點格式碼最多可以定義八組紋理坐标,紋理坐标資料由使用者在頂點資料中提供,資料通過索引值0到7來引用。最多可以有八個紋理混合層,一張紋理通過SetTexture(stageIndex, pTexture)與某一紋理層聯系在一起。

完成以上操作後,任意一組紋理坐标可以被任意一紋理層使用。每一組紋理坐标通過SetTextureStageState(stageIndex, D3DTSS_TEXCOORDINDEX, textureCoordinateIndex)與某一紋理層聯系在一起。請參閱IDirect3DDevice9::SetTextureStageState。通過這種方法,可以設定紋理混合層使它們使用任意一張紋理和任意一組紋理坐标。多個紋理層可以使用同一張紋理,或同一組紋理坐标。

以下主題包含了更多的資訊。

  • 直接把Texel映射到像素
  • 紋理坐标的格式
  • 紋理坐标的處理

直接把Texel映射到像素

應用程式經常需要把紋理貼到幾何體上并使texel直接映射到螢幕上的像素。例如,以一個需要在紋理中顯示文本的應用程式為例,為了使紋理中的文本能清晰地顯示,應用程式需要以某種方式確定映射到幾何體上的texel不受紋理過濾的影響。如果無法保證這一點,那麼得到的圖像通常會是模糊的,如果紋理過濾方法為最近點取樣,那麼可能會産生粗糙邊緣。

為了統一像素和紋理取樣,并同時支援圖像和紋理過濾,Microsoft® Direct3D®的像素和紋理取樣規則經過了精心定義,但這也使得把紋理中的texel直接映射到螢幕上的像素成為一個相當有意義卻又艱難的挑戰。要戰勝這個挑戰,需要透徹地了解Direct3D如何把用浮點數表示的紋理坐标映射到光栅化器使用的整數像素坐标。

為了把用浮點數表示的紋理坐标映射到texel位址,Direct3D執行下面的計算。

在這些公式中,Tx和Ty為水準/垂直方向的輸出texel坐标,u和v為頂點提供的水準/垂直方向的紋理坐标。Mx和My元素表示目前mipmap級水準/垂直方向的texel的數量。本節剩餘部分将主要讨論在水準方向上從texel到像素的映射,垂直方向上的映射與水準方向完全相同。

把紋理坐标0.0和1.0代入以上公式,會使紋理坐标0.0映射到本次紋理疊代的第一個texel和上次紋理疊代的最後一個texel的中間,紋理坐标1.0會被映射到本次紋理疊代的最後一個texel和下次紋理疊代的第一個texel的中間。對于一個寬度為4的疊代紋理,在mipmap的第0級,下圖顯示了系統把坐标0.0和1.0映射到哪裡。

了解了這種映射方式,應用程式隻需給幾何體的螢幕空間坐标加上一個偏移量,就可以強制系統把每個texel映射到相應的像素。例如,要繪制一個四邊形并使前面的紋理中的每個texel一一映射到螢幕上唯一的像素,應用程式必須使幾何體坐标覆寫所有像素,并使每個texel的中心正好對應每個像素的中心,這樣得到的結果就是應用程式常要追求的一對一映射。

為了把寬度為4的紋理映射到像素坐标0到3,可以用兩個三角形繪制一個四邊形,三角形在螢幕空間的坐标為-0.5到3.5,紋理坐标為0.0到1.0。以螢幕空間坐标為0.0的像素為例,因為0.0與第一個頂點,位于螢幕空間-0.5,相距半個像素,而總的寬度為4.0,疊代後的紋理坐标為0.125(譯注:每個像素的寬度為0.25=1/4,半個像素的寬度為0.125),然後用紋理的大小,此處為4,對紋理坐标進行縮放,得到的結果坐标為0.5。再減去偏移量0.5即得到紋理位址為0.0,該位址完全對應于貼圖中的第一個texel。

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

系統以相同的方法歸一化像素坐标和紋理坐标,是以,如果頂點與要渲染的像素重疊,且頂點使用的紋理坐标為0.0和1.0,那麼像素和頂點就可以對齊。如果它們的大小相同且排列整齊,那texel和像素就可以完全對應,如下圖所示。

紋理坐标的格式

Microsoft® Direct3D®中的紋理坐标可以包含一個、兩個、三個或四個用浮點數表示的元素,用來尋址不同大小的紋理。一維紋理——紋理表面的大小為1xn個texel——通過一個紋理坐标尋址。最常見的情況是二維紋理,通過兩個被稱為u和v的紋理坐标尋址。Direct3D支援兩種三維紋理,立方體環境貼圖和立體貼圖。立方體環境貼圖不是真正三維的,但使用一個三元素向量尋址。更多細節,請參閱立方體環境貼圖。

如頂點格式中所述,應用程式把紋理坐标編碼在頂點格式中。頂點格式可以包含多組紋理坐标。可以用FVF碼D3DFVF_TEX0到D3DFVF_TEX8描述不包含紋理坐标或最多可包含八組紋理坐标的頂點格式。

每組紋理坐标可以有一到四個元素。D3DFVF_TEXTUREFORMAT1到D3DFVF_TEXTUREFORMAT4标志用來描述一組紋理坐标中元素的個數,但這些标志不能直接使用,D3DFVF_TEXCOORDSIZEn系列宏使用這些标志建立位掩碼,用來在頂點格式中描述某組紋理坐标中元素的個數。這些宏帶一個參數,表示要設定元素個數的那組紋理坐标的索引值。以下示例代碼顯示了如何使用這些宏。

// 這個頂點格式包含兩組紋理坐标。第一組(索引為0)包含2個元素,

// 第二組包含一個元素。頂點格式描述應該是:

//     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;   // 第一組紋理坐标,二維

    float     t;      // 第二組紋理坐标,一維

} CustomVertexFormat;

注意 除了立方體貼圖和立體貼圖外,光栅化器無法使用兩個以上的元素尋址紋理。應用程式最多可以提供三個元素給一組紋理坐标,但隻有當紋理是立方體貼圖或立體貼圖,或使用了D3DTTFF_PROJECTED紋理變換标志時多餘的元素才會被用到。D3DTTFF_PROJECTED标志會使光栅化器把前兩個元素除以第三個(或第n個)元素。更多資訊,請參閱紋理坐标變換。

紋理坐标的處理

下圖顯示了紋理坐标從資料源,經過處理然後到達光栅化器的流程。

系統可以從兩個資料源得到紋理坐标。對于某一層紋理,應用程式可以使用包含在頂點格式(D3DFVF_TEX1到D3DFVF_TEX8)中的紋理坐标,也可以使用Microsoft® Direct3D®自動生成的紋理坐标。對于後一種情況,請參閱自動生成的紋理坐标。如果目前紋理層的D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀态為D3DTTFF_DISABLE(預設值),那麼系統不會對輸入的紋理坐标進行變換。如果D3DTSS_TEXTURETRANSFORMFLAGS為任何其它值,那麼系統會用該紋理層的變換矩陣對該輸入紋理坐标進行變換。

D3DTEXTURETRANSFORMFLAGS枚舉類型定義了D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀态的有效值。除了D3DTTFF_DISABLE标志不進行紋理坐标變換外,該枚舉類型值用于設定系統傳送至光栅化器的輸出坐标的數量。D3DTTFF_COUNT1到D3DTTFF_COUNT4标志告訴系統把輸出紋理坐标中的一個、兩個、三個或四個元素傳送至光栅化器。

D3DTTFF_PROJECTED标志有些特别:它告訴系統紋理坐标将用于經過投影的紋理。把D3DTTFF_PROJECTED标志與D3DTEXTURETRANSFORMFLAGS的其它成員一起使用可以告訴系統在光栅化操作前把所有元素除以最後一個元素。例如,當顯式使用三元素紋理坐标,或紋理變換産生三元素紋理坐标時,應用程式可以同時使用D3DTTFF_COUNT3和D3DTTFF_PROJECTED标志,這會使光栅化器把前兩個元素除以最後一個元素,并得到尋址二維紋理所需的二維紋理坐标。

注意 除了立方體貼圖和立體貼圖外,光栅化器無法使用多于兩個元素的紋理坐标。如果應用程式提供的元素比尋址目前紋理層所需的多,那麼多餘的元素會被忽略。當把二維紋理坐标用于一維紋理時也是這樣。

以下主題包含了更多的資訊。

  • 自動生成的紋理坐标
  • 紋理坐标變換
  • 特效

自動生成的紋理坐标

系統可以使用經過變換的錄影機空間中的位置或頂點的法向量作為紋理坐标,也可以計算用于尋址立方體貼圖的三元素向量。與應用程式在頂點資料中明确給出的紋理坐标一樣,應用程式可以使用自動生成的紋理坐标作為紋理變換的輸入。

通過無需在頂點格式中顯式地給出紋理坐标,自動生成的紋理坐标可以顯著降低幾何資料所需的帶寬。在許多情況下,系統生成的紋理坐标可以和紋理變換一起使用以生成特效。當然這隻是一種特殊用途,大多數情況下應用程式還是要用顯式給出的紋理坐标。

設定自動生成的紋理坐标

C++應用程式用D3DTSS_TEXCOORDINDEX紋理層狀态(來自D3DTEXTURESTAGESTATETYPE)控制系統如何産生紋理坐标。

一般來說,這個狀态告訴系統使用編碼在頂點格式中的某組特定的紋理坐标。當應用程式給這個狀态指定的值包含D3DTSS_TCI_CAMERASPACENORMAL,D3DTSS_TCI_CAMERASPACEPOSITION,或D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR标志時,系統執行的操作會完全不同。如果使用了這些标志中的任意一個,那麼紋理層會忽略在頂點格式中的給出的紋理坐标,并優先使用系統生成的坐标。下表列出了每個标志的意義。

  • D3DTSS_TCI_CAMERASPACENORMAL

使用變換到錄影機空間的頂點法向作為輸入紋理坐标。

  • D3DTSS_TCI_CAMERASPACEPOSITION

使用變換到錄影機空間的頂點位置作為輸入紋理坐标。

  • D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

使用變換到錄影機空間的反射向量作為輸入紋理坐标。反射向量根據輸入頂點位置和法向量計算得到。

前面的這些标志是互斥的。如果應用程式指定了其中一個标志,那麼應用程式還可以指定一個索引值,系統用這個索引值決定紋理環繞模式。

以下示例代碼顯示了如何在C++應用程式中使用這些标志。

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX,

                                   D3DTSS_TCI_CAMERASPACEPOSITION | 1 );

自動生成的紋理坐标在作為紋理坐标變換的輸入,或使應用程式無需計算用于尋址立方體環境貼圖的三元素向量時最為有用。

球形貼圖使用一張預計算的(在模組化時)紋理貼圖,該貼圖包含了一個反光的球體反射的整個環境。Microsoft® Direct3D®有一個紋理坐标自動生成特性,使用了D3DTSS_TCI_CAMERASPACENORMAL渲染狀态,該特性會取得變換到錄影機空間的頂點法向,并對它進行紋理變換生成紋理坐标。更多資訊請參閱Sphere Map示例。

相關主題

  • 紋理坐标變換
  • 立體環境貼圖

紋理坐标變換

Microsoft® Direct3D®裝置可以用一個4x4矩陣對頂點的紋理坐标進行變換。系統使用相同的方法對紋理坐标和幾何體進行變換。任何變換(縮放、旋轉、投影、shear或這些變換的組合)可以用一個4x4矩陣完成。

注意 Direct3D不改變經過變換和光照處理的頂點,是以,使用經過變換和光照處理的頂點的應用程式無法讓Direct3D變換頂點的紋理坐标。

支援硬體變換和光照的裝置(TnLHAL裝置)也會對紋理坐标變換進行硬體加速。如果裝置不支援硬體變換和光照,那麼Direct3D會使用幾何流水線中與平台相關的優化進行紋理坐标變換。

紋理坐标變換非常有用,它在生成特效的同時避免了對幾何體紋理坐标的直接修改。應用程式可以使用簡單的平移或旋轉矩陣給物體表面的紋理生成動畫效果,也可以對Direct3D自動生成的紋理坐标進行變換,這樣可以簡化并可能加速如投影紋理和動态光照貼圖等進階特效。另外,在多層紋理中,應用程式也可以用紋理坐标變換重複使用某一組紋理坐标并将之用于多種用途。

設定及取得紋理坐标變換的資訊

和應用程式用于變換幾何體的矩陣一樣,應用程式可以通過IDirect3DDevice9::SetTransform和IDirect3DDevice9::GetTransform方法設定和取得紋理坐标變換的資訊。在調用這些方法時,用D3DTRANSFORMSTATETYPE枚舉類型的成員D3DTS_TEXTURE0到D3DTS_TEXTURE7辨別紋理層0到7。

以下示例代碼設定了一個矩陣,對紋理層0的紋理坐标進行變換。

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

D3DMATRIX matTrans = D3DXMatrixIdentity( NULL );

// 為希望的變換設定矩陣。

d3dDevice->SetTransform( D3DTS_TEXTURE0, &matTrans );

啟用紋理坐标變換

D3DTSS_TEXTURETRANSFORMFLAGS紋理層狀态控制對紋理坐标的變換。這個紋理層狀态的值由D3DTEXTURETRANSFORMFLAGS枚舉類型定義。

當D3DTSS_TEXTURETRANSFORMFLAGS為D3DTTFF_DISABLE(預設值)時,紋理坐标變換被禁用。假設紋理層0的紋理坐标變換已啟用,以下代碼将之禁用。

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

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_DISABLE );

D3DTEXTURETRANSFORMFLAGS定義的其它值用于啟用紋理坐标變換,并控制把結果紋理坐标中的幾個元素傳送到光栅化器。以下為示例代碼。

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

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_COUNT2 );

D3DTTFF_COUNT2值告訴系統對紋理層0進行變換,然後把得到的紋理坐标的前兩個元素傳送給光栅化器。

D3DTTFF_PROJECTED紋理變換标志表示用于投影紋理的坐标。當這個标志被設定時,光栅化器會把傳入的元素除以最後一個元素。以下為示例代碼。

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

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_COUNT3 | D3DTTFF_PROJECTED );

本例告訴系統傳送三個紋理坐标元素到光栅化器。光栅化器把前兩個元素除以第三個元素,得到尋址紋理所需的二維紋理坐标。

特效

本主題包含了可以用紋理坐标處理實作的特效。

  • 給模組化上的紋理産生動畫效果(通過變換或旋轉)
  • 把紋理坐标作為模組化在錄影機空間中的位置的線性函數建立
  • 用立方體環境貼圖實作環境貼圖
  • 實作投影紋理

給模組化表面的紋理生成動畫效果(通過變換或旋轉)

  • 在頂點格式中定義一組二維紋理坐标。

·                // 使用單紋理,二維紋理坐标。這個位掩碼會根據需要

·                // 被擴充為包含位置,法向和顔色資訊。

DWORD dwFVFTex = D3FVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);

  • 設定光栅化器,使之使用二維紋理坐标。

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

  • 定義并設定合适的紋理坐标變換矩陣

// M為要設定的D3DMATRIX,用于在U和V方向對紋理坐标進行變換。

//      1   0 0 0

//      0   1 0 0

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

//      0   0 0 1

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

M._31 = du;

M._32 = dv;

把紋理坐标作為模組化在錄影機空間中的位置的線性函數建立

  • 用D3DTSS_TCI_CAMERASPACEPOSITION标志告訴系統使用頂點在錄影機空間中的位置作為紋理變換的輸入。

·                // 為了節省帶寬,輸入頂點沒有紋理坐标。三個紋理坐标用頂點在

·                // 錄影機空間中的位置(x, y, z)産生。

SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);

  • 告訴光栅化器使用二維紋理坐标。

·                // 使用了兩個輸出紋理坐标。

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

  • 定義并設定生成線性函數的矩陣。

// 把紋理坐标作為線性函數建立,這樣的話:

//      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);

用立方體貼圖實作環境貼圖

  • 用D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR标志告訴系統自動産生紋理坐标,并用作立方體貼圖的反射向量。

SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);

  • 告訴光栅化器使用三元素的紋理坐标。

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT3);

實作投影紋理

  • 使用D3DTSS_TCI_CAMERASPACEPOSITION标志告訴系統用頂點在錄影機空間中的位置作為紋理變換矩陣的輸入。

SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);

  • 建立紋理變換矩陣并執行變換,這超出了本文檔的範圍,計算機圖形工業中有許多文章讨論這個主題。
  • 告訴光栅化器使用三元素投影紋理坐标。

·                // 使用了兩個輸出紋理坐标。

·                 

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTF_PROJECTED | D3DTTFF_COUNT3);

紋理過濾

Microsoft® Direct3D®在渲染圖元時,要把三維圖元映射到二維螢幕上。如果圖元貼有紋理,那麼Direct3D必須用該紋理給圖元在二維螢幕上對應的每個像素産生一個顔色。對于每個像素,Direct3D必須從紋理獲得一個顔色值,這個過程被稱為紋理過濾。

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

Direct3D把複雜的紋理過濾過程簡化了,它給應用程式提供了三種類型的紋理過濾——線性過濾、各向異性過濾和mipmap過濾。如果應用程式不選擇以上三種紋理過濾,那麼Direct3D使用一種被稱為最近點取樣的技術。

每種類型的紋理過濾都各有優缺點。例如,線性紋理過濾可能會在最終圖像中産生鋸齒邊緣或馬賽克,但它是三種紋理過濾方法中計算量最小的。用mipmap紋理過濾通常可以得到最好的效果,尤其是和各向異性過濾一起使用的時候,但是在Direct3D支援的紋理過濾技術中,它對記憶體的需求也最大。

使用紋理接口指針的應用程式應該調用IDirect3DDevice9::SetSamplerState方法設定目前的紋理過濾方法。第一個參數為從0到7的整數,表示要設定紋理過濾方法的紋理層的索引值。第二個參數為D3DSAMP_MAGFILTER,D3DSAMP_MINFILTER或D3DSAMP_MIPFILTER,分别表示放大、縮小和mipmap過濾。第三個參數為D3DTEXTUREFILTERTYPE枚舉類型值,為要設定的紋理過濾方法。

本節介紹了Direct3D支援的紋理過濾方法,并被劃為以下主題。

  • 最近點取樣
  • 線性紋理過濾
  • 各向異性紋理過濾
  • 用Mipmap進行紋理過濾

注意 雖然D3DRENDERSTATETYPE枚舉類型中定義的紋理過濾渲染狀态已經被紋理層狀态取代,但是如果應用程式試圖使用這些渲染狀态的話,IDirect3DDevice9與IDirect3DDevice2不同的是,IDirect3DDevice9::SetRenderState方法不會失敗。相反,系統會把這些渲染狀态映射到多重紋理的第一層,也就是索引值為0的那層。應用程式不應該把老的渲染狀态和它們對應的紋理層狀态混在一起,這樣可能會産生無法預知的結果。

最近點取樣

應用程式不一定要使用紋理過濾。應用程式可以讓Microsoft® Direct3D®先計算紋理位址(通常都不是整數),然後使用離該值最近的整數位址處的texel的顔色,這個過程被稱為最近點取樣。如果紋理的大小與圖元在螢幕上的大小相近的話,那麼這将是快速且有效的紋理處理方法,但如果不是這樣的話,得到的圖像可能會有馬賽克、鋸齒或模糊不清。

C++應用程式可以調用IDirect3DDevice9::SetSamplerState方法選擇最近點取樣,隻需把第三個參數設定為D3DTEXF_POINT即可。

應用程式在使用最近點取樣時應該小心,因為當在兩個texel間的邊界處進行紋理取樣時,這種方法有時會産生圖形殘留物。當使用最近點取樣時,系統要麼取樣一個texel,要麼取樣另一個,這樣當紋理位址從一個texel移向下一個texel時,取樣得到的texel會突然改變。這種效果會導緻在最終顯示的紋理中出現不希望的圖形殘留物。當使用線性過濾時,取樣得到的texel是根據所有鄰近的texel計算得到的,當紋理位址在鄰近的texel間移動時,線性過濾會根據目前紋理位址與鄰近texel間的位置關系,把相鄰texel混合。

當把非常小的紋理貼到非常大的多邊形表面時可以看到這種效果:這個操作通常被稱為紋理放大(magnification)。例如,當使用的紋理看起來像西洋跳棋的棋盤時,最近點取樣會得到一個非常大的棋盤,格子之間有清晰的邊緣,相比之下,線性紋理過濾得到的圖像中棋盤的顔色會沿着多邊形逐漸改變。

大多數情況下,為了得到最好的效果,應用程式應該盡量避免使用最近點取樣。當今的大多數硬體都為線性過濾做了優化,是以應用程式不必擔心是以導緻的性能下降。如果應用程式想要的效果一定要用最近點取樣——比如用紋理顯示可讀的文本——那麼應用程式應該極度小心,避免在texel邊界取樣,因為那樣會導緻不想得到的效果。下圖顯示了這些圖形殘留物可能的樣子。

注意這組圖檔中右上角的兩個方塊與其餘的不同,可以在它們的對角線上看到明顯的偏移。要避免此類圖形殘留物,開發人員必須熟悉Direct3D在最近點取樣時使用的紋理取樣規則。Direct3D把閉區間[0.0, 1.0]範圍内的浮點紋理坐标映射到texel空間中的整數位址[–0.5, n – 0.5]範圍内,這裡n為給定紋理的大小。得到的紋理位址被舍入到最近的整數,這種映射方法在texel邊界會産生取樣誤差。

舉個簡單的例子,設想應用程式用D3DTADDRESS_WRAP紋理尋址模式渲染多邊形。根據Direct3D使用的映射方法,對于寬度為4個texel的紋理,紋理在u方向上的映射如下。

注意這張圖中的紋理坐标0.0和1.0,正好在texel之間的邊界。根據Direct3D的映射方法,紋理坐标的範圍為[–0.5, 4 – 0.5],這裡4為紋理的寬度。在這個例子中,紋理坐标為1.0處取樣得到的texel是texel 0。但是,如果紋理坐标隻比1.0稍微小一點,那麼取樣得到的就會是texel n而不是texel 0。

這就意味着用正好等于0.0和1.0的紋理坐标去放大較小的紋理,并把它貼到整齊排列在螢幕上的三角形時,如果過濾方法為最近點取樣方法,那麼得到的像素可能會在texel之間的邊界進行取樣。在紋理坐标計算過程中的任何誤差,無論多小,都可能在渲染得到的圖像上與texel邊界對應的部分出現圖形殘留物。

要完全精确地執行從浮點紋理坐标到整數texel位址的映射是很難的,也很耗時,并且通常是不必要的。大多數硬體實作在給三角形内的每個像素計算紋理坐标時使用疊代法。疊代法趨向于隐藏誤差,因為誤差在疊代過程中被均勻地累積起來。

Direct3D參考光栅化器在給每個像素計算紋理位址時使用直接指派法。直接指派法與疊代法的不同之處在于這種方法産生的誤差分布更随機。因為參考光栅化器不進行完全精确的計算,是以這種方法會導緻在邊界處的取樣誤差更容易被察覺。

最好的方法是隻在必要的時候使用最近點取樣。如果應用程式必須使用這種方法,那麼最好把紋理坐标稍微偏移一些使之離開邊界位置,這樣就可以避免殘留物的産生。

線性紋理過濾

Microsoft® Direct3D®使用一種被稱為雙線性過濾的線性紋理過濾方法。和最近點取樣一樣,雙線性過濾首先計算一個texel位址,這通常都不會是整數,然後找到離該位址最近的整數位址。另外,Direct3D渲染子產品還會根據最近取樣點上、下、左、右的texel計算它們的權重平均值。

可以調用IDirect3DDevice9::SetSamplerState方法選擇雙線性過濾,隻需把第三個參數設為D3DTEXF_LINEAR即可。

各向異性紋理過濾

因為三維物體表面與螢幕間的夾角而造成的紋理扭曲被稱為各向異性。當把一個各向異性的圖元所對應的像素映射到texel時,像素的形狀會被扭曲。Microsoft® Direct3D®根據像素被反向映射到紋理空間中的伸長率——也就是長度除以寬度——計量螢幕上像素的各向異性屬性。

為了提高渲染品質,應用程式可以把各向異性過濾與線性紋理過濾或mipmap紋理過濾結合在一起使用。應用程式可以調用IDirect3DDevice9::SetSamplerState方法啟用各向異性紋理過濾,隻需把第三個參數設為D3DTEXF_ANISOTROPIC即可。

應用程式必須同時把degree of anisotropy設為大于一的值。可以調用IDirect3DDevice9::SetSamplerState方法設定這個值。第一個參數為0到7的紋理層索引值,把第二個參數設為D3DSAMP_MAXANISOTROPY,第三個參數為要設定的degree of anisotropy(譯注:原文為degree of isotropy)的值。

應用程式隻需把degree of anisotropy(譯注:原文為degree of isotropy)設為一即可禁用各向異性過濾。要确定degree of anisotropy的允許範圍,可以檢查D3DCAPS9結構的MaxAnisotropy成員。

用Mipmap進行紋理過濾

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

較高分辨率的mipmap圖像用于離使用者近的物體,而較低分辨率的圖像則用于遠處的物體。使用Mipmap在消耗更多記憶體的情況下,提高了渲染得到的紋理的品質。

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

下圖顯示了這樣的例子。該紋理表示在一個三維第一人稱遊戲中的一個容器上的标記。建立mipmap時,最高分辨率的紋理是mipmap中的第一個,mipmap中每個随後的紋理的寬度和高度都是原來的一半,在這個例子中,最高分辨率的mipmap為256x256。下一級紋理為128x128,最後一級紋理為64x64。

這個标記有一個最遠可見距離。如果使用者離标記很遠,那麼遊戲就用mipmap鍊中最小的紋理顯示,在這個例子中就是64x64的紋理。

随着使用者移動視點并離标記越來越近,遊戲會逐漸使用mipmap鍊中更高分辨率的紋理。下圖中紋理的分辨率為128x128。

當視點離标記的距離為所允許的最近距離時,遊戲就使用最高分辨率的紋理。

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

Direct3D可以确定mipmap鍊中哪一級紋理的分辨率與目前需要的最為接近,并把像素映射到那一級紋理的texel空間。如果最終圖像需要的分辨率位于mipmap鍊中兩級紋理的分辨率之間,Direct3D會取得這兩級mipmap中的texel并把它們的顔色值混合在一起。

要使用mipmap,應用程式必須建立一個mipmap鍊。應用程式隻需把mipmap鍊設為目前紋理可以使用mipmap。更多資訊,請參閱紋理混合。

下一步,應用程式必須設定Direct3D用于取樣texel的紋理過濾方法。Mipmap過濾最快的方法就是讓Direct3D選擇最近的texel,D3DTEXF_POINT枚舉類型值就是用來選擇這種方法的。如果應用程式使用D3DTEXF_LINEAR枚舉類型值,那麼Direct3D可以産生更好的過濾效果,這會使Direct3D選擇最近的那級mipmap,然後根據目前像素在那一級mipmap中所映射的texel及其附近的texel計算權重平均值。

Mipmap紋理用于減少渲染三維場景所需的時間,同時提高了場景的真實感。但是,mipmap通常需要大量的記憶體。

建立mipmap鍊

以下示例代碼顯示了應用程式如何調用IDirect3DDevice9::CreateTexture方法建立一條五級mipmap鍊:256x256, 128x128, 64x64, 32x32, 及16x16。

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

IDirect3DDevice9::CreateTexture的前兩個參數為最高一級的紋理的寬和高。第三個參數為mipmap紋理的級數,如果應用程式把它設為零,那麼Direct3D會建立一系清單面,每個都是前一個的一半,直到大小為1x1為止。第四個參數指定該資源的用途,本例中,零表示不為該資源指定特殊的用途。第五個參數指定紋理的表面格式,該參數為D3DFORMAT枚舉類型值。第六個參數為D3DPOOL枚舉類型值,表示在把建立的資源放在哪種類型的記憶體中,除非應用程式使用動态紋理,否則建議使用D3DPOOL_MANAGED。最後一個參數為指向IDirect3DTexture9接口指針的位址。

注意    Mipmap鍊中的每個表面的大小都是前一個表面的一半。如果最高一級mipmap的大小為256x128,那麼第二級的mipmap就是128x64,第三級為64x32,依次類推,直到1x1(譯注:最後幾級分别為:4x2,,2x1,1x1)。應用程式在Levels中要求的mipmap級不能使鍊中的任何mipmap的寬和高小于1。舉個簡單的例子,如果最高一級的mipmap表面為4x2,那麼Levels的最大允許值就是三,第三層的大小為1x1。如果Levels的值大于3,那麼會導緻第二級mipmap的高度值出現小數,而這是不允許的。(譯注:原文表述不夠準确,應該是log2 (max (width, height) ) < 0)

選擇并顯示mipmap

可以調用IDirect3DDevice9::SetTexture方法把mipmap紋理設定為目前紋理中的第一個紋理,更多資訊請參閱紋理混合。

應用程式在選擇mipmap紋理後,必須用D3DTEXTUREFILTERTYPE枚舉類型值設定D3DSAMP_MIPFILTER取樣器狀态。之後Direct3D就可以自動執行mipmap紋理過濾。以下示例代碼顯了如何啟用mipmap紋理過濾。

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

應用程式也可以手工周遊mipmap鍊,隻需調用IDirect3DTexture9::GetSurfaceLevel方法,并指定要取得的mipmap級即可。以下示例代碼從mipmap鍊的最高一級周遊到最低一級。

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

為了把位圖資料載入mipmap鍊中的每個表面,應用程式需要手工周遊mipmap鍊,一般來說這是周遊mipmap鍊的唯一原因。應用程式可以通過調用IDirect3DBaseTexture9::GetLevelCount取得mipmap的級數。

紋理資源

紋理資源在IDirect3DTexture9接口中實作。要得到一個指向紋理接口的指針,應該調用IDirect3DDevice9::CreateTexture方法或以下Direct3D擴充(D3DX)函數。

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

以下示例代碼調用D3DXCreateTextureFromFile從檔案Tiger.bmp中載入一張紋理。

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

LPDIRECT3DTEXTURE9 pTexture;

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

D3DXCreateTextureFromFile的第一個參數為指向IDirect3DDevice9接口的指針。第二個參數為檔案名,告訴Direct3D從哪個檔案載入紋理。第三個參數為指向IDirect3DTexture9接口的指針的位址,表示建立得到的紋理對象。

用紋理資源進行渲染

Direct3D通過紋理層的概念支援多重紋理混合,每個紋理層包含一張紋理以及可以在這張紋理上執行的操作。紋理層中的紋理組成了一個目前紋理的集合。更多資訊請參閱紋理混合。每張紋理的狀态被封裝在對應的紋理層中。

C++應用程式必須調用IDirect3DDevice9::SetTextureStageState方法設定每張紋理的狀态。第一個參數為從0到7的紋理層索引值,第二個參數為D3DTEXTURESTAGESTATETYPE枚舉類型值,最後一個參數為要設定的紋理層狀态值。

在使用紋理接口指針進行渲染時,應用程式最多可以把八張紋理混合。應用程式通過調用IDirect3DDevice9::SetTexture方法設定目前紋理。Direct3D會先把所有目前紋理混合,然後再貼到正在渲染的圖元的表面。

注意 因為IDirect3DDevice9::SetTexture方法會增加正在設定的紋理表面的參考計數(reference count),是以當不再需要該紋理時,應用程式應該把相應紋理層的紋理設定為NULL。如果應用程式不這樣做,那麼表面就不會被釋放,并造成記憶體洩漏。

應用程式可以調用IDirect3DDevice9::SetRenderState方法設定目前紋理的紋理環繞狀态,隻需把第一個參數設為從D3DRS_WRAP0到D3DRS_WRAP7的值,并把第二個參數設為D3DWRAPCOORD_0,D3DWRAPCOORD1,D3DWRAPCOORD2,及D3DWRAPCOORD3标志的組合,這樣就可以啟用在u, v, 或w方向上的紋理環繞。

應用程式還可以設定紋理透視和紋理過濾狀态。請參閱紋理過濾。

紋理環繞

簡而言之,紋理環繞用來改變Microsoft® Direct3D®用每個頂點的紋理坐标對貼有紋理的多邊形進行光栅化的基本方法。在光栅化一個多邊形時,為了确定多邊形所覆寫的每個像素所需使用的 texel,系統要在每個多邊形頂點的紋理坐标之間進行插值。一般來說,系統把紋理當做二維平面,為了在紋理中的點A和點B之間插值得到新的texel,系統會取兩點間的最短路徑。如果點A表示的u, v坐标為(0.8, 0.1),點B表示的u, v坐标為(0.1,0.1),那麼插值的路徑會如下圖所示。

注意這張圖中點A和點B間的最短路徑大緻穿越紋理的中央。啟用u方向或v方向上的紋理坐标環繞會相應改變Direct3D在u方向或v方向上對紋理坐标間最短路徑的了解。紋理環繞的定義使光栅化器在擷取紋理坐标間的最短路徑時,假設紋理坐标0.0和1.0是等價的。最後這一點是技巧所在:可以這樣想象,在某一方向上啟用紋理環繞會使系統把紋理當成在圓柱體表面環繞的紋理進行處理。例如,考慮下圖。

這幅圖顯示了在u方向上的環繞是怎樣影響系統對紋理坐标間的插值的。在一張普通的,或沒有環繞的紋理上使用與前例中相同的點,我們會發現點A和點B間的最短路徑不再穿越紋理的中央,而是穿越紋理的邊框,也就是紋理坐标0.0和1.0重合的地方。在v方向上的環繞與此類似,唯一的不同在于圓柱體是平躺的。同時在u方向和v方向上進行環繞比較複雜,在這種情況下,可以把紋理想象成是一個立體圓環。

紋理環繞最通常的應用是在使用環境貼圖時。通常,使用環境貼圖的物體會顯得比較反光,并反射出物體周圍的場景。為了便于讨論,我們想象一個房間,房間有四面牆,每面牆上寫着字母R, G, B, Y,分别對應顔色紅,綠,藍,黃。這樣一個簡單的房間使用的環境貼圖可能如下圖所示。

設想房屋的屋頂由一根完全反光的柱子支撐,柱子有四面。要把環境貼圖貼到柱子上很簡單,但是要讓柱子看起來像是反射牆上的字母和顔色就沒有那麼容易了。下圖顯示了用線框模式繪制的柱子,并在頂部的頂點處列出了相應的紋理坐标。環繞将發生在紋理的接縫處,在下圖中用虛線表示。

如果在u方向上啟用紋理環繞,那麼柱子會正确地顯示出環境貼圖上的顔色和字母,并且在紋理前面的接縫處,光栅化器會假設u坐标0.0和1.0是等價的并正确地選擇紋理坐标間的最短路徑。貼上紋理後的柱子如下圖所示。

如果沒有啟用紋理環繞,那麼光栅化器就不會産生可信的反射圖像。相反,柱子前面的部分會包含經過擠壓的位于u坐标0.175到0.875之間的texel,因為這些texel穿越紋理的中央。這樣環繞效果被破壞了。

使用紋理環繞

要啟用紋理環繞,應該調用IDirect3DDevice9::SetRenderState方法,如以下示例代碼所示。

d3dDevice->SetRenderState(D3DRS_WRAP0, D3DWRAPCOORD_0);

IDirect3DDevice9::SetRenderState的第一個參數是要設定的渲染狀态,應該設為從D3DRS_WRAP0到D3DRS_WRAP7的枚舉類型值,表示要設定哪一個紋理層的環繞狀态。把第二個參數設為從D3DWRAPCOORD_0到D3DWRAPCOORD_3的标志可以在對應的方向上啟用紋理環繞,也可以組合使用這些标志在多個方向上啟用紋理環繞。如果應用程式忽略其中某個标志,那麼在相應的方向上的紋理環繞就被禁用,要禁用某組紋理坐标在所有方向上的紋理環繞,隻需把第二個參數設為0。

不要把紋理環繞與名字相近的紋理尋址模式相混淆。紋理環繞在對紋理進行尋址之前進行。一定要保證用于紋理環繞的資料不包含[0.0, 1.0]範圍外的紋理坐标,因為那樣會導緻不希望的結果。有關紋理尋址的更多資訊,請參閱紋理尋址模式。

位移貼圖的環繞

位移貼圖由tessellation引擎解釋,因為無法為tessellation引擎指定環繞模式,是以不能對位移貼圖進行紋理環繞。應用程式可以用一組頂點,強制進行在任何方向上的環繞。應用程式也可以指定隻進行簡單的線性插值。

紋理混合

Microsoft® Direct3D®最多可以在一趟渲染過程中把八張紋理混合并貼到圖元上。使用多重紋理可以極大地提高Direct3D應用程式的執行速度。應用程式可以用多重紋理混合在一趟渲染過程中産生紋理、影子、鏡面反射光、漫反射光,以及其它特效。

要使用紋理混合,應用程式必須先檢查硬體是否支援紋理混合。這個資訊包含在D3DCAPAS9結構的TextureCaps成員中。有關如何查詢硬體的紋理混合能力的細節,請參閱IDirect3DDevice9::GetDeviceCaps。

紋理層和紋理混合級聯

通過使用紋理層,Direct3D支援在一趟渲染過程中完成多重紋理混合。一個紋理層有兩個輸入,并對它們執行一個混合操作,然後把結果用于進一步的處理或用于光栅化。可以把紋理層想象為如下圖所示。

如上圖所示,紋理層用一個指定的操作符把兩個輸入混合。常用的操作符包括對輸入參數的顔色或阿爾法的簡單調制或相加,但實際上Direct3D支援幾十種混合操作。紋理層的輸入可以是與該層關聯的紋理,疊代後的顔色或阿爾法(在進行高洛德着色的過程中疊代得到),指定的顔色或阿爾法,或前一個紋理層的結果。更多資訊,請參閱紋理混合操作和輸入。

注意 Direct3D區分顔色混合和阿爾法混合。應用程式分别設定要對顔色和阿爾法執行的混合操作及相應的輸入,并且這些設定互不影響。

多重混合層的參數和操作的組合定義了一種簡單的基于流程的混合語言。每一層的結果流入下一層,依次類推。這個概念,也就是混合的結果在層與層之間流動,并最終被用來對多邊形進行光栅化操作,通常被稱為紋理混合級聯。下圖顯示了各獨立的紋理層如何組成紋理混合級聯。

裝置中的每個紋理層都有一個從零開始的索引值。雖然應用程式應該總是檢查裝置的能力以确定目前裝置支援幾層紋理,但是Direct3D最多允許有八個混合層。第一層的索引值為0,第二層的索引值為1,依次類推,直到索引值7。系統根據索引值的增長混合各紋理層。

最好隻使用需要的那些混合層,預設情況下沒有用到的混合層被禁用。是以,如果應用程式隻使用前兩個混合層,那麼隻需設定層0和層1的操作符和參數。系統會對這兩層執行混合操作,并忽略其餘被禁用的層。

有關性能的注意事項    如果應用程式在不同的情況下使用不同數量的紋理層——比如對一些物體使用四層紋理,而對其它物體隻使用兩層紋理——那麼應用程式無需顯式地禁止所有以前使用過的層。一種選擇是對未曾用到的紋理層的第一層,禁用其顔色操作符,這樣該紋理層及其後的紋理層将不會被用到。另一種選擇是設定第一層紋理(層0)的顔色操作符,禁用所有紋理貼圖。第三種選擇是當紋理層的D3DTSS_COLORARG1等于D3DTA_TEXTURE時,隻要該層的紋理指針為空,該層及其後的紋理層都不會被處理。

以下主題包含了更多資訊。

  • 紋理混合操作及參數
  • 設定目前紋理
  • 建立混合層
  • 阿爾法紋理混合
  • 多趟紋理混合
  • 用紋理實作光照貼圖

紋理混合操作及參數

應用程式把混合層與目前紋理集合中的每張紋理相聯系。Microsoft® Direct3D®按照順序對每個混合層求值,從集合中的第一張紋理開始,至第八張結束。

Direct3D把目前紋理集合中每張紋理的資訊應用于與之相聯系的混合層。通過調用IDirect3DDevice9::SetTextureStageState,應用程式可以控制使用紋理層中的哪些資訊。應用程式可以分别設定對顔色和阿爾法通道的操作,每個操作有兩個參數。用D3DTSS_COLOROP紋理層狀态指定要對顔色通道執行的操作,用D3DTSS_ALPHAOP紋理層狀态指定要對阿爾法通道執行的操作,這兩個紋理層狀态都是D3DTEXTUREOP枚舉類型值。

紋理混合的參數使用D3DTEXTURESTAGESTATETYPE枚舉類型的D3DTSS_COLORARG1, D3DTSS_COLORARG2, D3DTSS_ALPHAARG1和D3DTSS_ALPHAARG2成員表示。對應的參數值由D3DTA指定。

注意 通過把某一層的顔色操作設定為D3DTOP_DISABLE,應用程式可以禁用該紋理層及紋理混合級聯中所有的後續層。禁用顔色操作會同時禁用阿爾法操作。當顔色操作被啟用時,阿爾法操作無法被禁用。當顔色混合被啟用時,把阿爾法操作設定為D3DTOP_DISABLE會導緻不确定的結果。

要測定一個裝置支援的紋理混合操作,請查詢D3DCAPS9結構的TextureCaps成員。

設定目前紋理

Microsoft® Direct3D®維護着一個目前紋理清單,最多可以有八張。Direct3D會把這些紋理混合到要渲染的圖元上。隻有作為紋理接口指針建立的紋理可以被用于目前紋理集合。

應用程式可以調用IDirect3DDevice9::SetTexture方法把紋理設定到目前紋理集合中。第一個參數必須是閉區間0到7之間的數字,第二個參數是紋理接口指針。

以下C++示例代碼顯示了如何把一張紋理加入到目前紋理集合中。

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

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

// 設定第三層紋理

d3dDevice->SetTexture(2, pTexture);

注意 軟體裝置不支援同時把一張紋理指定到一個以上的紋理層。

建立混合層

一個混合層是一個紋理操作及相應參數的集合,它定義了怎樣混合紋理。C++應用程式在建立混合層時調用IDirect3DDevice9::SetTextureStageState方法。第一次調用指定要執行的操作,另外兩次調用指定參數,Direct3D将用這兩個參數執行指定的操作。以下示例代碼描述了如何建立一個混合層。

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

// 設定要對第一個紋理進行的操作

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

// 将參數1設定為紋理顔色

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

// 将參數2設定為疊代後的漫反射色。

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

紋理中的texel資料包含顔色和阿爾法值。應用程式可以在一個混合層中分别定義顔色和阿爾法操作。顔色操作和阿爾法操作分别有自己的參數。更多細節,請參閱D3DTEXTURESTAGESTATETYPE。

雖然下面的宏并不是Microsoft® Direct3D®應用程式程式設計接口(API)的一部分,但是應用程式可以用它們簡化建立紋理混合層所需的代碼。

#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®在渲染一個圖元時會根據圖元的材質、或圖元的頂點顔色,及光照資訊為該圖元産生一個顔色。更多細節,請參閱光照與材質。如果應用程式啟用紋理混合,那麼Direct3D必須把經過處理的多邊形上的像素顔色與已經儲存在幀緩存中的像素顔色進行混合。Direct3D使用以下公式計算圖元的(渲染得到的)圖像中每個像素的最終顔色。

FinalColor = TexelColor × SourceBlendFactor + PixelColor × DestBlendFactor

在這個公式中,FinalColor為輸出到目标渲染表面的最終像素顔色。TexelColor為經過紋理過濾的輸入顔色值,對應目前像素。有關Direct3D如何把texel映射到像素的細節,請參閱紋理過濾。SourceBlendFactor為一個經過計算的值,Direct3D用它計算輸入顔色值在最終顔色中所占的百分比。PixelColor為目前存儲在圖元的圖像中像素的目前顔色。DestBlendFactor為目前像素顔色在最終渲染得到的像素中所占的百分比。SourceBlendFactor和DestBlendFactor的值在閉區間0.0到1.0範圍内。

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

經過紋理過濾後,每個像素的顔色值包含紅、綠和藍三種顔色值。預設情況下,Direct3D使用D3DBLEND_SRCALPHA作為SourceBlendFactor,使用D3DBLEND_INVSRCALPHA作為DestBlendFactor。是以,通過設定紋理中的阿爾法值,應用程式可以控制處理後的像素的透明度。

C++應用程式可以用D3DRS_SRCBLEND和D3DRS_DESTBLEND渲染狀态控制這些因子,隻需調用IDirect3DDevice9::SetRenderState方法,把這兩個渲染狀态其中之一作為第一個參數傳入。第二個參數必須是D3DBLEND枚舉類型成員。

多趟紋理混合

通過在多趟渲染的過程中将多個紋理貼到一個圖元的表面,Microsoft® Direct3D®應用程式可以實作許多特效,這通常被稱為多趟(multipass)紋理混合。多趟紋理混合的一個典型用途就是通過把幾個不同紋理上的顔色混合,模拟複雜的光照和着色模型的效果。這種應用被稱為光照貼圖。更多資訊,請參閱用紋理實作光照貼圖。

注意 一些裝置可以在一趟渲染過程中将多張紋理貼到圖元表面。細節請參閱紋理混合。

如果使用者的硬體不支援多重紋理混合,應用程式可以使用多趟紋理混合以達到同樣的視覺效果。但是,與使用多重紋理混合相比,應用程式将無法保持相同的幀速率。

要進行多趟紋理混合,C++應用程式應該執行以下操作。

  1. 調用IDirect3DDevice9::SetTexture方法給紋理層0設定一張紋理。
  2. 調用IDirect3DDevice9::SetTextureStageState方法設定相應的顔色和阿爾法混合操作及參數。預設的設定就很适合用于多趟紋理混合。
  3. 渲染場景中相應的物體。
  4. 将下一張紋理指定到紋理層0。
  5. 根據需要設定D3DRS_SRCBLEND和D3DRS_DESTBLEND渲染狀态以調整源和目的混合因子。系統根據這些參數把新的紋理和已經存在于渲染目标表面中的像素進行混合。
  6. 根據所需紋理的數量,重複步驟3,4,5。

用紋理實作光照貼圖

對于想要真實地渲染一個三維場景的應用程式來說,必須要考慮光源會對渲染得到的場景産生的效果。雖然諸如平面着色和高洛德着色之類的技術在這方面也是有用的工具,但它們可能無法滿足應用程式的要求。Microsoft® Direct3D®支援多趟和多重紋理混合。與僅使用着色技術相比,這些能力使應用程式能夠渲染更具真實感的場景。通過使用一張或多張光照貼圖,應用程式可以把光影的範圍映射到圖元上。

光照貼圖是包含三維場景中光照資訊的一張紋理或一組紋理。應用程式可以把光照資訊存放在光照貼圖的阿爾法值中,顔色值中,或以上兩者中。

如果應用程式用多趟紋理混合實作光照貼圖,應用程式應該在第一趟渲染時把光照貼圖貼到圖元上,在第二次渲染時使用基本紋理。鏡面反射光照貼圖是個例外,在這種情況下,要先渲染基本紋理,再添加光照貼圖。

多重紋理混合使應用程式能一次同時渲染光照貼圖和基本紋理。如果使用者的硬體支援多重紋理混合,應用程式應該在進行光照貼圖時利用這一特性,這将極大地提高應用程式的性能。

如果使用光照貼圖,Direct3D應用程式可以在渲染圖元時得到多種光照效果。應用程式不僅可以在場景中使用單色光和有色光,還可以添加諸如鏡面反射高光和漫反射光之類的細節。

以下主題介紹了用Direct3D紋理混合實作光照貼圖的資訊。

  • 單色光照貼圖
  • 有色光照貼圖
  • 鏡面反射光照貼圖
  • 漫反射光照貼圖

單色光照貼圖

一些老的三維加速卡不支援使用目标像素的阿爾法值進行紋理混合,更多資訊請參閱阿爾法紋理混合。一般來說這些加速卡也不支援多重紋理混合,如果應用程式在此類擴充卡上運作,那麼可以用多趟紋理混合進行單色光照貼圖。

要進行單色光照貼圖,應用程式應該把光照資訊存放在光照貼圖的阿爾法資料中。應用程式使用Microsoft® Direct3D®的紋理過濾功能把圖元的圖像中的每個像素映射到光照貼圖中的相應texel。應用程式應該把源混合因子設為相應texel的阿爾法值。

以下C++示例代碼描述了應用程式如何把一張紋理用作單色光照貼圖。

// 本例假設d3dDevice為指向IDirect3DDevice9接口的有效指針,

// 且lptexLightMap為指向包含單色光照貼圖資料的紋理的有效指針。

// 把光照貼圖設定為目前紋理。

d3dDevice->SetTexture(0, lptexLightMap);

// 設定顔色操作。

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

// 設定顔色操作的第一個參數。

d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1,

       D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE);

因為不支援目标阿爾法混合的擴充卡一般來說也不支援多重紋理混合,這個示例把光照貼圖設為第一張紋理,而這在所有三維加速卡上都是可用的。示例代碼先設定紋理混合層的顔色操作,讓紋理資料與圖元已有的顔色進行混合,然後選擇第一張紋理和圖元已有的顔色作為輸入資料。

有色光照貼圖

如果應用程式使用有色光照貼圖,那麼通常會渲染得到更具真實感的三維場景。一張有色光照貼圖使用RGB資料存放光照資訊。

以下C++示例代碼顯示了如何用RGB顔色資料進行光照貼圖。

// 本例假設d3dDevice為指向IDirect3DDevice9接口的有效指針,

// 且lptexLightMap為指向包含單色光照貼圖資料的紋理的有效指針。

// 把光照貼圖設為第一張紋理。

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 );

這個示例先把光照貼圖設為第一張紋理,然後設定第一個混合層的狀态,對輸入資料進行調制(相乘),并把第一張紋理和圖元的目前顔色用作調制操作的參數。

鏡面反射光照貼圖

在對發亮的物體——那些使用了高反射度材質的物體——進行光照計算時會産生鏡面反射高光。在一些情況下,由光照子產品産生的鏡面反射高光不夠精确,為了産生更吸引人的鏡面反射高光,許多Microsoft® Direct3D®應用程式會給圖元使用鏡面反射光照貼圖。

要進行鏡面反射光照貼圖,隻需把鏡面反射光照貼圖與圖元的紋理相加,然後再和RGB光照貼圖進行調制(與結果相乘)操作。

以下C++示例代碼描述了這個過程。

// 本例假設d3dDevice為指向IDirect3DDevice9接口的有效指針。

// lptexBaseTexture為指向紋理的有效指針。

// lptexSpecLightMap為指向包含RGB鏡面反射光照貼圖資料的紋理的有效指針。

// lptexLightMap為指向包含RGB光照貼圖資料的紋理的有效指針。

// 設定基本紋理。

d3dDevice->SetTexture(0, lptexBaseTexture );

// 設定要對基本紋理執行的操作及參數。

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

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

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

// 設定鏡面反射光照貼圖。

d3dDevice->SetTexture(1, lptexSpecLightMap);

// 設定要對鏡面反射光照貼圖執行的操作及參數。

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

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

d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );

// 設定RGB光照貼圖。

d3dDevice->SetTexture(2, lptexLightMap);

// 設定要對RGB光照貼圖執行的操作及參數。

d3dDevice->SetTextureStageState(2,D3DTSS_COLOROP, D3DTOP_MODULATE);

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

d3dDevice->SetTextureStageState(2,D3DTSS_COLORARG2, D3DTA_CURRENT );

漫反射光照貼圖

不光滑的表面在被光源照射時會顯示漫反射光。漫反射光的亮度取決于表面到光源的距離及表面法向與光源的方向向量間的夾角。通過光照計算(譯注:由光照子產品執行)模拟漫反射光隻能得到一般的效果。

應用程式可以用光照貼圖模拟更為複雜的漫反射光照效果,隻需在基本紋理的基礎上再加一張漫反射光照貼圖即可,如以下C++示例代碼所示。

// 本例假設d3dDevice為指向IDirect3DDevice9接口的有效指針。

// lptexBaseTexture為指向紋理的有效指針。

// lptexLightMap為指向包含RGB光照貼圖資料的紋理的有效指針。

// 設定基本紋理。

d3dDevice->SetTexture(0,lptexBaseTexture );

// 設定要對基本紋理執行的操作及參數。

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

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

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

// 設定漫反射光照貼圖。

d3dDevice->SetTexture(1,lptexDiffuseLightMap );

// 設定混合層 。

d3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE );

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

d3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );

表面

表面是顯存的線性表示,雖然可以存在于系統記憶體中,但更通常都存在于顯示卡的顯存中。IDirect3DSurface9接口包含了表面對象。

可以通過調用下列方法得到一個IDirect3DSurface9接口。

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

IDirect3DSurface9接口允許應用程式通過IDirect3DDevice9::UpdateSurface方法間接通路記憶體。該方法允許應用程式從一個IDirect3DSurface9接口複制一塊矩形區域的像素到另一個IDirect3DSurface9接口。表面接口也提供了直接通路顯存的方法。例如,應用程式可以用IDirect3DSurface9::LockRect方法鎖定顯存的一塊矩形區域。很重要的一點是在完成對鎖定的矩形區域的操作後,要調用IDirect3DSurface9::UnlockRect方法。

表面格式用于描述如何解釋表面記憶體中的像素資料。Microsoft® Direct3D®使用D3DSURFACE_DESC結構的D3DFORMAT成員描述表面格式。應用程式可以通過調用IDirect3DSurface9::GetDesc方法取得一個現有的表面的格式。

以下主題包含了更多資訊。

  • 寬度與Pitch的比較
  • 翻轉表面
  • 頁面翻轉和後緩存
  • 複制到表面
  • 複制表面
  • 直接通路表面記憶體
  • 私有表面資料
  • Gamma控制

寬度與Pitch的比較

雖然對術語寬度和pitch的使用經常不很正式,但它們有着非常重要并且完全不同的意義。是以,開發人員應該了解它們的意義,以及如何解釋Microsoft® Direct3D®用于描述它們的值。

Direct3D使用D3DSURFACE_DESC結構儲存描述表面的資訊。其中,這個結構包含了表面的大小,以及這些大小在記憶體中是如何表示的。結構使用Height和Width成員描述表面的邏輯大小,這兩個成員都以像素為機關。是以,對于一個640x480表面來說,無論它是8位表面或24位RGB表面,它的Height和Width值都是相同的。

當應用程式使用IDirect3DSurface9::LockRect方法鎖定一個表面時,該方法會填寫一個D3DLOCKED_RECT結構,這個結構包含了表面的pitch值及一個指向被鎖定資料的指針。Pitch成員的值描述了表面在記憶體中的pitch,也被稱為跨度。Pitch是兩個記憶體位址間以位元組為機關的距離,兩個記憶體位址分别表示一個位圖某一行的起始位址以及下一行的起始位址。因為pitch是以位元組為機關而非以像素為機關,是以一個640x480x8表面的pitch值與另一個大小相同但像素格式不同的表面的pitch值會大不相同。另外,pitch值有時反映出被Direct3D保留并用作高速緩存的位元組數,是以簡單地認為pitch就是寬度乘以每個像素所占的位元組數是不保險的。如下圖所示,對寬度和pitch之間的差別做一個直覺的比較會更清楚。

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

當直接通路表面時要小心,不要通路為表面配置設定的記憶體以外的地方,更不要通路任何為高速緩存目的而保留的記憶體。另外,當應用程式鎖定一個表面的一部分時,應用程式必須保持在鎖定表面時指定的矩形區域内。不按這些指導方針行事将會導緻無法預料的結果。當直接渲染到表面記憶體時,應該總是使用由IDirect3DSurface9::LockRect方法傳回的pitch值。不要認為pitch僅取決于顯示模式。如果應用程式在一些顯示擴充卡上運作良好,但在另一些擴充卡上顯示不正确的話,很可能就是pitch的問題。

更多資訊,請參閱直接通路表面記憶體。

翻轉表面

Microsoft® Direct3D®應用程式一般通過這種方式顯示動畫序列,即:先在後緩存中生成動畫的各幀,然後按順序把這些幀顯示出來。後緩存是屬于交換鍊的一部分。一個交換鍊是一系列後緩存,這些後緩存會一個接一個被“翻轉”到螢幕上。這種方法可以用來在記憶體中渲染一個場景,當渲染完成後随即把場景翻轉到螢幕上。這避免了畫面撕裂的現象,并能生成更為平滑的動畫。

在Direct3D中建立的每個裝置至少有一個交換鍊。當應用程式初始化第一個Direct3D裝置時,應用程式要設定D3DPRESENT_PARAMETER結構的BackBufferCount成員,告訴Direct3D交換鍊需要包含的後緩存的數量。随後對IDirect3DDevice9::CreateDevice的調用會建立Direct3D裝置及相應的交換鍊。

當應用程式使用IDirect3DDevice9::Present方法要求一個翻轉操作時,指向前緩存的指針和指向後緩存的指針被交換。翻轉是通過切換顯示裝置用來引用記憶體的指針完成的,而不是複制表面的記憶體。當翻轉鍊包含一個前緩存和一個以上的後緩存時,指針的切換以循環的方式進行,如下圖所示。

通過調用IDirect3DDevice9::CreateAdditionalSwapChain,應用程式可以為裝置建立附加的交換鍊。應用程式可以為每個視區建立一個交換鍊并将每個交換鍊與某個特定視窗相關聯。應用程式在每個交換鍊的後緩存中渲染圖像,然後分别顯示它們。IDirect3DDevice9::CreateAdditionalSwapChain的兩個參數分别為一個指向D3DPRESENT_PARAMETER結構的指針和一個指向IDirect3DSwapChain9接口的指針。應用程式可以使用IDirect3DSwapChain9::Present顯示位于前緩存之後的那個後緩存的内容。注意一個裝置隻能有一個全屏交換鍊。

應用程式可以通過調用IDirect3DDevice9::GetBackBuffer或IDirect3DSwapChain9::GetBackBuffer方法取得對某個後緩存的通路權,這兩個方法會傳回一個指向IDirect3DSurface9接口的指針,代表被傳回的後緩存表面。注意對這兩個方法的調用會增加IDirect3DDevice9接口的内部引用計數,是以當完成對表面的操作後要記得調用IUnknown::Release,否則會導緻記憶體洩漏。

記住,Direct3D通過交換交換鍊内指向表面記憶體的指針翻轉表面,而不是交換表面本身。這意味着應用程式總是在下次将被顯示的那個後緩存上進行渲染。

很重要的一點是要注意由顯示卡驅動程式執行的“翻轉操作”和一個用D3DSWAPCHAIN_FLIP标志建立的交換鍊執行的“present”操作間的差別。

按照慣例,術語“翻轉”表示改變顯示卡用來産生輸出信号的視訊記憶體位址的範圍,這樣就導緻原先隐藏着的後緩存的内容将被顯示。在Microsoft DirectX® 9.0中,這個術語更經常地是被用來描述把任何用D3DSWAPEFFECT_FLIP标志建立的交換鍊中的後緩存顯示出來。

而當交換鍊為全屏模式時,“present”操作幾乎總是通過翻轉操作實作,當交換鍊為視窗模式時,“present”操作必然通過複制操作實作。此外,顯示卡驅動程式可能會根據D3DSWAPEFFECT_DISCARD和D3DSWAPEFFECT_COPY标志,用翻轉實作全屏交換鍊的present操作。

以上讨論适用于常用的情況,也就是用D3DSWAPEFFECT_FLIP标志建立的全屏交換鍊。

有關視窗和全屏交換鍊的各種不同交換效果的讨論,請參閱D3DSWAPEFFECT。

頁面翻轉和後緩存

頁面翻轉是多媒體、動畫和遊戲軟體中的關鍵,它和動畫師用一疊紙産生動畫的方法相似。在每張紙上,動畫師對圖檔稍做改變,是以當動畫師在頁與頁之間快速地翻動時,圖檔看起來就像是動了。

軟體中的頁面翻轉與這個過程相似。Microsoft® Direct3D®通過交換鍊實作頁面翻轉功能,而交換鍊是裝置的一個屬性。開始時,應用程式先設定一系列Direct3D緩存,這些緩存會以和動畫師相同的翻頁方法翻轉到螢幕。第一個緩存被稱為前顔色緩存,它之後的緩存被稱為後緩存。應用程式可以先寫入到後緩存,然後翻轉顔色緩存,這樣後緩存就顯示在螢幕上。當系統顯示圖像時,應用程式又可以寫入到後緩存。這個過程可以一直持續,這樣應用程式就可以高效地生成活動的圖像。

Direct3D使建立一個頁面翻轉機制非常容易——從一個雙緩存機制(一個前顔色緩存和一個後緩存)到使用額外後緩存的更為複雜的機制。

複制到表面

當使用IDirect3DDevice9::UpdateSurface時,要傳入源表面中的一個矩形,或者用NULL表示整個表面,應用程式還需要傳入目标表面中的一個點,源圖像的左上角将被複制到這個位置。該方法不支援裁剪,除非源矩形和對應的目标矩形分别完全被包含在源和目标表面内,否則操作将會失敗。這個方法不支援阿爾法混合、color key,及格式轉換。注意目标表面和源表面不能是同一個表面。

其它有關UpdateSurface的使用限制,請參閱IDirect3DDevice9::UpdateSurface。

C++/C應用程式可以用下列方法把圖像複制到一個Microsoft® Direct3D®表面。

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

相關主題

  • IDirect3DDevice9::StretchRect

複制表面

術語blit是“位塊傳輸(bit block transfer)”的縮寫,表示把資料塊從記憶體中的一處傳輸到另一處的過程。作為在每幀——IDirect3DDevice9::Present方法背後的面向複制的機制——中移動大塊矩形中的像素的主要機制,blitting裝置驅動程式接口(DDI)仍在繼續使用。blit操作中對紋理資料的傳輸由IDirect3DDevice9::UpdateTexture方法執行。在DirectX 9.0中,紋理資料也可以用IDirect3DDevice9::UpdateSurface方法複制,該方法複制像素的一個矩形子集。

注意 DirectX 9.0提供了Direct3D擴充(D3DX)函數,這使應用程式可以從檔案載入紋理,進行顔色轉換,及調整紋理的大小。有關更多可供使用的函數的資訊,請參閱與紋理相關的函數。

相關主題

  • IDirect3DDevice9::StretchRect

直接通路表面記憶體

通過使用IDirect3DSurface9::LockRect方法,應用程式可以直接通路表面記憶體。在調用這個方法時,pRect參數為指向RECT結構的指針,描述要直接通路表面上的哪一部分。如果要鎖定整個表面,隻需把pRect設為NULL即可。同時,應用程式可以指定一個隻覆寫表面的一部分的RECT。如果提供兩個不相交疊的矩形,那麼兩個線程或程序可以同時鎖定一個表面中的多個矩形。注意一個多重取樣的(multisample)後緩存不能被鎖定。

IDirect3DSurface9::LockRect方法會填寫一個D3DLOCKED_RECT結構,該結構中包含了通路表面記憶體所需的全部資訊。該結構包含了pitch資訊,及一個指向被鎖定的資料的指針。當應用程式完成對表面記憶體的通路後,應該調用IDirect3DSurface9::UnlockRect方法将表面解鎖。

應用程式在鎖定了一個表面後,可以直接對其中的内容進行操作。下面給出了一些提示,說明如何避免在使用直接渲染表面記憶體(directly rendering surface memory)時遇到的一些問題。

  • 絕對不要認為pitch是一個常數,應該總是檢查IDirect3DSurface9::LockRect方法傳回的pitch資訊。Pitch可能會因為各種原因而不同,包括表面記憶體所在的位置,顯示卡的類型,甚至是Microsoft® Direct3D®驅動程式的版本。更多資訊請參閱寬度與pitch的比較。
  • 應用程式應該保證隻對未鎖定的表面進行複制操作,如果是鎖定的表面,那麼Direct3D的複制操作将會失敗。
  • 當一個表面被鎖定時,應用程式應該限制對它進行的操作。
  • 在複制資料時,應用程式應該總是保證和顯存對齊。Microsoft Windows® 98使用了一個頁故障處理器,Vflatd.386,它為使用記憶體單元切換(bank-switched memory)的顯示卡實作一個虛拟的平面幀緩存。該處理器允許此類顯示裝置以線性方式把幀緩存提供給Direct3D。當複制與顯存不對齊的資料時,如果複制的資料跨越記憶體單元,那麼可能會導緻系統挂起。
  • 如果表面隸屬于D3DPOOL_DEFAULT記憶體池中的資源,那麼它可能無法被鎖定,除非它是動态紋理或是用IDirect3DDevice9::CreateOffscreenPlainSurface建立的表面。後緩存表面可以通過IDirect3DDevice9::GetBackBuffer和IDirect3DSwapChain::GetBackBuffer方法通路,隻有在建立交換鍊時,D3DPRESENT_PARAMETER結構的Flags成員包含了D3DPRESENT_LOCKABLE_BACKBUFFER标志的情況下,它們才可以被鎖定。

私有表面資料

應用程式可以在表面中存儲任何類型的應用程式特有的資料。例如,在一個遊戲中,一個表示地圖的表面可以包含有關地形的資料。

一個表面可以有一個以上的私有資料緩存。每個緩存用一個GUID辨別,該GUID由應用程式在把資料連接配接到表面時提供。

要存儲私有表面資料,應該使用SetPrivateData,并傳入源緩存,資料的大小,及應用程式為資料定義的GUID。或者,源資料也可以以COM對象的形式存在,這種情況下,應用程式隻需傳入對象的IUnknown接口指針,并設定D3DSPD_IUNKNOWNPOINTER标志。

SetPrivateData會為資料配置設定一塊内部緩存并把資料複制到其中。應用程式可以安全地釋放源緩存或對象。當FreePrivateData被調用時,内部緩存或對接口的引用也會被釋放。當釋放一個表面時,系統會自動執行這個操作。

要得到表面的私有資料,應用程式必須先配置設定一塊大小合适的緩存,然後調用GetPrivateData方法,并把原先賦給資料的GUID傳入。應用程式有責任釋放任何用于這塊緩存的動态記憶體。如果資料是COM對象,那麼該方法會取得IUnknown指針。

如果應用程式不知道應該配置設定多大的緩存,可以先把pSizeOfData設為零并調用GetPrivateData,如果調用失敗并傳回D3DERR_MOREDATA,那麼該方法會在pSizeOfData中傳回所需的位元組數。

Gamma控制

Gamma控制允許應用程式改變系統如何顯示表面的内容,同時不會影響表面本身的内容。可以認為這些控制是很簡單的過濾器,在把表面資料顯示在螢幕上之前,Microsoft® Direct3D®會對這些資料進行過濾。

Gamma控制是交換鍊的一個屬性。有了Gamma控制,動态改變如何把表面的紅、綠和藍色深映射到系統最終顯示的實際色深就成為了可能。通過設定Gamma level,應用程式可以使使用者的螢幕閃現不同的顔色——當使用者控制的角色被擊中時為紅色,當角色撿起了新的物品時為綠色,等等——同時不必為了達到相同的效果而把新的圖像複制到幀緩存中去。

由于在Microsoft DirectX® 9.0中,交換鍊是裝置的一個屬性,是以每個Direct3D裝置都至少有一條交換鍊(隐式交換鍊)。正因為gamma ramp是交換鍊的一個屬性,是以當交換鍊處于視窗模式下時,gamma ramp也可以使用。Gamma ramp會立刻生效,不存在等待VSYNC的操作。

IDirect3DDevice9::SetGammaRamp和IDirect3DDevice9::GetGammaRamp方法允許應用程式在把表面中的像素送到數模轉換器(DAC)進行顯示之前對ramp levels進行操作,這會影響到表面中的像素的紅、綠和藍色分量。

Gamma Ramp Levels

在Direct3D中,術語gamma ramp指的是一個組數值,這些數值用于把幀緩存中所有像素的某一顔色分量——紅、綠、藍——的level映射到被DAC接收并用于顯示的新的色深。

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

通過畫圖很容易就能把ramp值直覺地表示出來。下面兩張圖中左圖顯示了一個完全不改變顔色的ramp,右圖顯示的ramp會給它所作用于的顔色分量加上負偏移。

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

設定及取得Gamma Ramp Levels

Gamma ramp levels是Direct3D用于把幀緩存中的顔色分量映射到将被顯示新的level的快速查找表。應用程式可以通過調用IDirect3DDevice9::SetGammaRamp和IDirect3DDevice9::GetGammaRamp方法設定和取得主表面的ramp levels。IDirect3DDevice9::SetGammaRamp接收兩個參數,第一個參數為D3DSGR_CALIBRATE或D3DSGR_NO_CALIBRATION,第二個參數pRamp為一指向D3DGAMMARAMP結構的指針。D3DGAMMARAMP結構包含了三個有256個元素的WORD數組,每個數組用來存放紅、綠和藍gamma ramp。IDirect3DDevice9::GetGammaRamp接收一個參數,為一指向D3DGAMMARAMP的指針,目前的gamma ramp将被填寫到該指針指向的結構中。

應用程式可以把IDirect3DDevice9::SetGammaRamp的第一個參數設為DDSGR_CALIBRATE,這樣在設定新的gamma levels時就會調用校正器。由于校正gamma ramp會增加一些開銷,是以最好不要頻繁地調用。無論顯示卡或顯示器是何類型,設定一個校正過的gamma ramp會給使用者提供完全一緻的gamma值。

并非所有系統都支援gamma校正,要測定裝置是否支援gamma校正,應該調用IDirect3DDevice9::GetDeviceCaps,然後檢查由該方法傳回的D3DCAPS9結構的Caps2成員。如果設定了D3DCAPS2_CANCALIBRATEGAMMA能力标志,那麼裝置就支援gamma校正。

在設定新的ramp levels時,謹記應用程式在數組中設定的levels隻有當應用程式運作于全屏獨占模式時才會被使用。一旦應用程式切換到正常模式,ramp levels就會被忽略,并在應用程式恢複到全屏模式時重新生效。

即使裝置在目前presentation模式(全屏或視窗)下不支援gamma ramps,也不會傳回錯誤碼。應用程式可以檢查D3DCAPS9結構的Caps2成員是否設定了D3DCAPS2_FULLSCREENGAMMA和D3DCAPS2_CANCALIBRATEGAMMA能力位,以确定裝置的能力及是否安裝了校正器。

Mipmap的自動生成

Mipmap的自動生成在建立紋理時利用了硬體過濾,這使得這項功能對應用程式而言是完全透明的。自動生成對mipmap渲染目标尤其有用,因為它們位于顯存中,而這種情況下,軟體過濾的效率很低。

要自動生成 mipmap,應該在建立紋理時設定D3DUSAGE_AUTOGENMIPMAP标志。後續的sublevel的生成對應用程式來說都是完全透明的。在某些情況下,某些硬體的自動mipmap生成可能會占用許多時間,這時應用程式可以适時地使用IDirect3DBaseTexture9::GenerateMipSubLevels,示意驅動程式生成相應數量的sublevels。

IDirect3DBaseTexture9::SetAutoGenFilterType用來控制自動生成時的過濾品質。改變過濾類型會導緻mipmap的sublevels被标記為無效的并需要重新生成。

IDirect3DBaseTexture9::GetAutoGenFilterType用來取得目前的過濾類型。在建立紋理時使用的預設過濾類型是D3DTEXF_LINEAR。如果驅動程式不支援線性過濾,那麼過濾類型将被設為D3DTEXF_POINT。

如果紋理不是用D3DUSAGE_AUTOGENMIPMAP标志建立的,那麼調用這些方法不會産生任何效果,也不會傳回任何錯誤。除了D3DTEXF_NONE外,驅動程式支援的所有可用于正常紋理過濾的過濾類型都可用于自動生成。對每種資源類型而言,驅動程式應該支援它在相應的紋理、立方體紋理和立體紋理過濾能力資訊中提供的過濾類型。

當源紋理是自動生成的mipmap,而目标紋理不是時,IDirect3DDevice9::UpdateTexture是非法的,調用會失敗。如果源紋理不是自動生成的mipmap且目标紋理是自動生成的mipmap,那麼隻有目标紋理中的最高的相比對的那層被更新,該層的sublevels會重新被生成,而源紋理的sublevels将會被忽略。與此類似,如果源紋理和目标紋理都是自動生成的mipmap,那麼隻有目标紋理中的最高的相比對的那層被更新,該層的sublevels會被重新生成,而源紋理的sublevels将被忽略。

在建立一個自動生成的mipmap時,Levels參數必須被設定為零或一。

要檢查裝置對自動mipmap生成的支援,應該檢查裝置是否設定了D3DCAPS2_CANAUTOGENMIPMAP能力位。如果是,那麼應該用D3DUSAGE_AUTOGENMIPMAP作為參數調用IDirect3D9::CheckDeviceFormat方法。如果傳回值為D3D_OK,那麼可以保證mipmap是自動生成的。如果傳回值是D3DOK_NOAUTOGEN,這意味着對建立紋理的調用會成功,但不會生成任何mipmap。

要知道裝置支援哪些過濾類型,應該檢查D3DCAPS9結構的TextureFilterCaps、CubeTextureFilterCaps(譯注:及VolumeTextureFilterCaps)成員所包含的能力位。

最後,要注意D3DUSAGE_AUTOGENMIPMAP隻是一個提示,在建立紋理或調用 IDirect3D9::CheckDeviceFormat的過程中指定這個标志不會在任何類型的裝置驅動程式接口(DDI)上引起錯誤。

相關主題

  • D3DUSAGE
  • D3DCAPS2
  • D3DTEXTUREFILTERTYPE

自動紋理管理

紋理管理是确定在某一特定時刻需要用哪些紋理進行渲染,并確定這些紋理已經被載入顯存的過程。同任何算法一樣,不同的紋理管理機制在複雜度上會有所不同,但任何紋理管理機制都會涉及到以下一些關鍵任務。

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

為了保證紋理載入具有最佳的性能,Microsoft® Direct3D®内建了對紋理管理的支援。由Direct3D管理的紋理資源被稱為由系統管理的資源。

紋理管理器用時間戳對紋理進行跟蹤,時間戳記錄了紋理最後被使用的時間。管理器然後用最近最少使用(least-recently-used)算法确定哪些紋理應該被移除。在準備把兩張紋理從顯存中移除時,紋理的優先級用來仲裁。如果兩張紋理具有相同的優先級,那麼最近最少使用的那張紋理會被移除。如果兩張紋理具有相同的時間戳,那麼優先級較低的那張紋理會先被移除。

應用程式可以在建立紋理表面時要求自動紋理管理。要在C++應用程式中得到一個由系統管理的紋理,應該調用IDirect3DDevice9::CreateTexture建立紋理資源,并把Pool參數指定為D3DPOOL_MANAGED。Direct3D不允許應用程式指定要在何處建立紋理。在建立由系統管理的紋理時,應用程式不能使用D3DPOOL_DEFAULT或D3DPOOL_SYSTEMMEM标志。建立完由系統管理的紋理後,應用程式可以調用IDirect3DDevice9::SetTexture方法把紋理設到渲染裝置的紋理級聯中。

應用程式可以通過調用IDirect3DDevice9::SetPriority方法給由系統管理的紋理設定優先級。

Direct3D根據需要自動把紋理載入顯存。系統可能會根據非本地視訊記憶體的可用性或其它因素把由系統管理的紋理放在本地或非本地視訊記憶體中作為高速緩存。系統不會把由系統管理的紋理所用的緩存的位置和大小告訴應用程式,而且對使用自動紋理管理而言也無需了解該資訊。如果應用程式使用的紋理超過了顯存所能容納的數量,那麼Direct3D會把舊的紋理從顯存中移除以給新的紋理騰出空間。如果應用程式再次用到被移除的紋理,那麼系統會用原始的系統記憶體紋理表面把紋理重新載入到顯存的高速緩存中。雖然重新載入紋理是必須的,但它同時降低了應用程式的性能。

通過更新或鎖定紋理資源,應用程式可以動态地修改紋理位于系統記憶體中的原件。當系統檢測到一個無效表面時——在更新操作完成後,或當表面被解鎖時——紋理管理器會自動地更新紋理位于顯存中的複本。由此導緻的性能下降與重新載入一個被移除的紋理相似。

當進入遊戲中新的一關時,應用程式可能需要清空顯存中所有由系統管理的紋理,此時應該調用IDirect3DDevice9::EvictManagedResources。

有關資源管理的更多資訊,請參閱管理資源。

壓縮紋理資源

紋理貼圖是畫在三維物體上的數字化圖像,用來添加可視細節。它們在光栅化時被貼到物體表面,這個過程會消耗大量的系統帶寬和記憶體。為了減少紋理所消耗記憶體的數量,Microsoft® Direct3D®支援對紋理表面的壓縮。一些Direct3D裝置本身就支援壓縮紋理表面。在這些裝置上,隻要應用程式建立了壓縮表面并将資料載入其中,該表面就可以和其它任何紋理表面一樣,在Direct3D中使用。在把壓縮紋理貼到三維物體表面時,Direct3D會進行解壓。

存儲效率和紋理壓縮

所有紋理壓縮的格式都是二的乘方。雖然這并不表示紋理一定要是方的,但确實表示X和Y都是二的乘方。例如,如果一個紋理原來是512×128,那下一級mipmap應該是256×64,依次類推,每一級都以兩倍遞減。到最低兩級,紋理被過濾成16×2 和8×1,因為壓縮塊總是一個4×4的texel塊,是以這裡會浪費一些資料位。塊中沒有用到的部分被填滿。雖然在最低幾級會浪費一些資料位,但總體的收獲還是顯著的。理論上最差的情況是,一個2K×1的紋理。這裡,每一塊隻用到一行像素,其餘的都沒有用到。

在單個紋理内的混用不同格式

需要特别注意的是任何單個的紋理必須指明它的資料——每組16個texel——是以64位還是以128位存儲的。如果是64位塊——也就是說,紋理用了DXT1格式,那麼在同一紋理内以塊為機關,混用不透明和一位阿爾法格式是可以的。換句話說,對每個由16個texel組成的塊,對color_00和color_1兩個無符号整數的比較是單獨進行的。

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

對用于三維模組化的紋理,Direct3D提供了壓縮表面的服務。本節提供了有關建立壓縮紋理表面及操控表面中的資料的資訊。

資訊被分為以下主題。

  • 不透明和一位阿爾法紋理
  • 帶阿爾法通道的紋理
  • 壓縮紋理格式
  • 使用壓縮紋理

不透明和一位阿爾法紋理

DXT1紋理格式用于不透明的或隻有一個透明色的紋理。

每個不透明或一位阿爾法塊儲存了兩個16位顔色值(RGB 5:6:5格式)和一個4x4的位圖,位圖中的每個像素占用2位。這樣16個texel一共占用64位,或每個像素占用四位。在位圖塊中,每個texel占用2位,可以選擇四個顔色,其中兩個直接存儲在經過編碼的資料中,另兩個則通過線性插值從存儲的顔色值導出。下圖顯示了這種布局。

可以通過對存儲在塊中的兩個16位顔色值進行比較來區分一位阿爾法格式和不透明格式。兩個16位顔色值被當作無符号整數。如果第一個顔色值大于第二個,那麼就暗示這一塊隻定義了不透明texel。這意味着有四個顔色可以用來表示texel。在四色編碼中,有兩個是導出的顔色,四個顔色值在RGB顔色空間中均勻分布。這種格式和RGB 5:6:5格式相似。否則(第一個顔色值小于等于第二個),就是一位阿爾法格式,一位阿爾法格式可以使用三個顔色,第四個顔色被保留,用來表示透明的texel。

在三色編碼中,有一個導出的顔色,第四個2位編碼被保留,用來表示透明的texel(阿爾法資訊)。這種格式與RGBA 5:5:5:1格式相似,RGBA 5:5:5:1格式的最後一位被用來編碼阿爾法掩碼。

以下示例代碼描述了用來決定目前塊是使用了三色編碼還是四色編碼的算法。

if (color_0 > color_1)       
{      
   // 四色編碼塊:導出另兩個顔色。      
    // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3      
    // 這些二位編碼對應于存儲在64位塊中的二位位域。      
    color_2 = (2 * color_0 + color_1 + 1) / 3;      
    color_3 = (color_0 + 2 * color_1 + 1) / 3;      
}          
else      
{       
    // 三色編碼:導出一個顔色。      
    // 00 = color_0, 01 = color_1, 10 = color_2,       
    // 11 = transparent.      
    // 這些二位編碼對應于存儲在64位塊中的二位位域。      
     color_2 = (color_0 + color_1) / 2;          
     color_3 = transparent;          
}      

應用程式在進行混合操作之前,最好把透明像素的RGBA成員設為零。

下面這些表顯示了八位元組塊的記憶體布局,這裡假設第一個索引值對應y坐标,第二個索引值對應x坐标。例如,Texel[1][2]指的是紋理貼圖中位于(x,y) = (2,1)處的像素。

下表顯示了八位元組(64位)塊的記憶體布局。

字位址 16位字
Color_0
1 Color_1
2 位圖資料Word_0
3 位圖資料Word_1

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

顔色
4:0 (最低位) 藍色分量
10:5 綠色分量
15:11 紅色分量

位圖資料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]

位圖資料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]

不透明顔色編碼的示例

作為不透明編碼的一個例子,假設位圖左右兩邊的顔色為紅色和黑色。紅色為color_0,黑色為color_1。在它們之間有兩個插值得到的顔色,四個顔色一起形成了均勻的顔色變化。以下計算用來确定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      

位圖看起來如下圖所示。

這看起來就是下面的一系列顔色。

一位阿爾法編碼的示例

當16位無符号整數color_0小于color_1時,就表示選擇一位阿爾法格式。舉個例子,這種格式可以用來顯示在藍天背景前的樹葉,可以把一些texel标記成透明的,而樹葉還可以使用三種深度的綠色。其中兩個顔色位于兩端,第三個顔色通過插值得到。

這裡顯示了這樣一幅圖檔。

注意圖像中白色的地方,texel會被編碼成透明的。此外還要注意在進行混合前,應該把透明texel的RGBA分量設為零。

以下計算用來确定位圖的顔色和透明度的編碼。

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

位圖看起來如下圖所示。

帶阿爾法通道的紋理

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

透明度塊和顔色塊中資料的排列如下表所示。

字位址 64位塊
3:0 透明度塊
7:4 前述64位塊

直接紋理編碼

對于直接紋理編碼(DXT2和DXT3格式),描述texel的透明度的阿爾法分量被編碼在一個4x4位圖中,每個texel占用4位。這四位阿爾法值可以通過各種方法得到,諸如抖動或使用阿爾法資料的最高四位。但無論它們是如何産生的,都隻按原樣使用,不做任何形式的插值。

下圖顯示了一個64位的透明度塊。

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

下面這些表描述了在每個16位字中,阿爾法資訊的記憶體布局。

下表包含了字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之間的差別在于,在DXT2格式中,我們認為顔色資料已經預乘了阿爾法,而在DXT3格式中,我們認為顔色資料沒有預乘阿爾法。之是以需要這兩種是格式是因為大多數情況下,在使用一個紋理時,僅檢查資料并不足以确定顔色資料是否已經預乘了阿爾法。因為運作的時候需要這樣的資訊,是以就用兩個四字元碼(FOURCC)來區分這兩種情況。但是,這兩種格式使用的資料和插值方法是相同的。

為了檢測texel是否是透明的,在DXT1中要對顔色進行比較,而DXT2和DXT3格式沒有使用種比較。我們認為在不需要顔色比較的情況下,顔色資料總是以四色模式進行處理。換句話說,DXT1代碼中最上面的if語句應該是:

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

三位線性阿爾法插值

對DXT4和DXT5格式的透明度的編碼基于線性插值,這和顔色編碼中使用的線性編碼相似。這兩種編碼方法中,第一個八位元組塊存儲了兩個8位阿爾法值和一個4x4位圖,位圖中的每個像素占用三位。存儲的兩個阿爾法值用來插值導出中間的阿爾法值。其它資訊根據兩個阿爾法值的存儲方式有所不同。如果alpha_0大于alpha_1,那麼插值會導出六個中間的阿爾法值。反之,插值會導出四個中間的阿爾法值,另外兩個隐含的阿爾法值分别為0(完全透明)和255(完全不透明)。

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

// 含8個阿爾法還是含6個阿爾法的塊?       
if (alpha_0 > alpha_1) {          
    // 含八個阿爾法的塊:導出另外六個阿爾法值。       
    // 位編碼000 = alpha_0, 001 = alpha_1, 其它值通過插值得到。      
    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 {       
    // 含六個阿爾法的塊。       
    // 位編碼000 = alpha_0, 001 = alpha_1, 其它值通過插值得到。      
    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      
}      

阿爾法塊的記憶體布局如下所示:

位元組 阿爾法
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之間的差別在于,在DXT4格式中,我們認為顔色資料已經預乘了阿爾法,而在DXT5格式中,我們認為顔色資料沒有預乘阿爾法。之是以需要這兩種是格式是因為大多數情況下,在使用一個紋理時,僅檢查資料不足以确定顔色資料是否已經預乘了阿爾法。因為運作的時候需要這樣的資訊,是以就用兩個四字元碼(FOURCC)來區分這兩種情況。但是,這兩種格式使用的資料和插值方法是相同的。

為了檢測texel是否是透明的,在DXT1中要對顔色進行比較,而DXT4和DXT5格式沒有使用這種比較。我們認為在不需要顔色比較的情況下,顔色資料總是以四色模式進行處理。換句話說,DXT1代碼中最上面的if語句應該是:

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

壓縮紋理格式

本節包含了有關壓縮紋理格式的内部結構的資訊。應用程式開發人員并不需要了解這些細節以使用壓縮紋理,因為應用程式可以使用Direct3D擴充(D3DX)函數在壓縮和未壓縮紋理間進行轉換。但是,如果應用程式需要直接對壓縮表面中的資料進行操作,那麼這些資訊就很有用。

Microsoft® Direct3D®使用的壓縮格式把紋理貼圖分成4x4的texel塊。如果紋理不包含透明度——也就是說是不透明的——或者透明度用一位阿爾法表示,那麼就用一個8位元組的塊表示紋理貼圖塊。如果紋理貼圖确實在阿爾法通道中包含了透明度資訊,那麼就用一個16位元組的塊表示紋理貼圖塊。

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

注意    任何單個紋理必須指定它的資料——每組16個texel——是以64位還是以128位存儲的。如果紋理用了64位塊——也就是DXT1格式,那麼可以以塊為機關在同一張紋理中混合使用不透明和一位阿爾法格式。換句話說,對每個由16個texel組成的塊來說,對無符号整數color_0和color_1的比較是單獨進行的。

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

目前DXTn格式的pitch與Microsoft DirectX® 7.0中傳回的不同。該值現在指的是以塊為機關的每一行的pitch。例如,如果應用程式有一個寬度為16的壓縮紋理,那麼pitch值會是四塊(DXT1為4*8,DXT2-5為4*16)。

使用壓縮紋理

測定對壓縮紋理的支援

在應用程式建立一個渲染裝置前,可以通過調用IDirect3D9::CheckDeviceFormat方法測定裝置是否支援用壓縮紋理進行紋理操作。該方法測定是否可以在裝置上使用某種表面格式。

要測試擴充卡,應該把像素格式指定為DXT1, DXT2, DXT3, DXT4, 或DXT5四字元碼。如果IDirect3D9::CheckDeviceFormat傳回D3D_OK,那麼裝置可以從該格式的壓縮紋理直接建立紋理。如果是這樣,那麼應用程式就可以通過調用IDirect3DDevice9::SetTexture方法,在Microsoft® Direct3D®中直接使用壓縮紋理表面。以下示例代碼顯示了如何測定擴充卡是否支援壓縮紋理格式。

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

如果裝置不支援用壓縮紋理表面進行紋理操作,那麼應用程式仍然可以在壓縮格式的表面中存儲資料,但是必須在使用紋理之前,把任何壓縮紋理轉換為裝置支援的格式。

建立壓縮紋理

在建立了一個支援壓縮紋理格式的裝置後,應用程式就可以建立壓縮紋理資源了。隻要調用IDirect3DDevice9::CreateTexture并在Format參數中指定一個壓縮紋理格式即可。

在把圖像載入紋理對象之前,應該調用IDirect3DTexture9::GetSurfaceLevel方法取得一個指向紋理表面的指針。

現在應用程式就可以調用任何D3DXLoadSurfacexxx函數,把圖像載入先前用IDirect3DTexture9::GetSurfaceLevel取得的表面。

用DirectX®軟體開發包(SDK)提供的DXTex工具,開發人員可以建立壓縮紋理(DDS)檔案并在不同的格式間進行轉換。

這種方法的好處在于應用程式可以把壓縮表面的内容存到檔案中,而不必根據表面的格式以及紋理的寬度和高度計算所需的存儲容量。

下表顯示了五種壓縮紋理類型。更多有關資料存儲方式的資訊,請參閱壓縮紋理格式。開發人員隻有在要寫自己的壓縮函數時才需要了解這些資訊。

四字元碼 描述 預乘阿爾法?
DXT1 不透明/一位阿爾法 N/A
DXT2 直接阿爾法
DXT3 直接阿爾法
DXT4 插值阿爾法
DXT5 插值阿爾法

注意 當應用程式要把資料從未預乘阿爾法格式傳輸到已預乘阿爾法格式時,Direct3D會根據阿爾法值縮放顔色值。Direct3D不支援把資料從已預乘阿爾法格式傳輸到未預乘阿爾法格式。如果應用程式試圖把資料從已預乘阿爾法的源資料傳輸到未預乘阿爾法的目标資料時,被調用的方法會傳回D3DERR_INVALIDCALL。如果應用程式要把資料從已預乘阿爾法的源資料傳輸到不含阿爾法的目标資料時,那麼源資料中已預乘過阿爾法的顔色分量會被直接複制,不做任何改變。

壓縮紋理的解壓

正如對紋理表面的壓縮,對壓縮紋理的解壓也是通過Direct3D的複制服務進行的。

要把壓縮紋理複制到未壓縮的紋理表面,應該使用函數D3DXLoadSurfaceFromSurface。這個函數會處理對表面的壓縮和解壓。

使用紋理時需要考慮的硬體問題

目前的硬體并不一定支援Microsoft® Direct3D®接口提供的全部功能。應用程式必須測試使用者的硬體并相應地調整渲染政策。

許多三維加速卡不支援把經過疊代的漫反射色作為混合單元的參數,但是,應用程式可以在進行紋理混合時使用經過疊代的顔色資料。

一些三維硬體的第一層紋理可能沒有相應的紋理混合層,在此類擴充卡上,應用程式應該在目前紋理集合的第二和第三紋理層進行混合操作。

因為目前大多數硬體的限制,很少有擴充卡可以通過IDirect3DDevice9提供的多重紋理混合接口執行三線性mipmap插值。應用程式可以用多趟紋理混合達到相同的效果,或者降級使用被廣為支援的D3DTEXF_POINT mipmap過濾模式。

立體紋理資源

立體紋理是三維像素(texel)的集合,可以用來繪制諸如三角形或線之類的二維圖元。對于每個使用立體紋理的圖元來說,圖元的每個頂點需要包含三元素紋理坐标。在繪制圖元時,圖元覆寫的每個像素的顔色來自立體紋理中的某個像素,這和二維紋理中的情況相一緻。因為不存在可以用立體紋理進行繪制的真正的三維圖元,是以立體紋理不能直接用來渲染。

應用程式可以把立體紋理用于諸如patchy fog,爆炸等特效。

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

建立立體紋理

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

首先,定義一個包含三個紋理坐标的自定義頂點類型,如以下示例代碼所示。

struct VOLUMEVERTEX

{

    FLOAT x, y, z;

    DWORD color;

    FLOAT tu, tv, tw;

};

#define D3DFVF_VOLUMEVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|

                             D3DFVF_TEX1|D3DFVF_TEXCOORDSIZE3(0))

然後,把資料填入頂點。

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 }

};

現在,建立一個頂點緩存,并填入頂點資料。

下一步是用IDirect3DDevice9::CreateVolumeTexture建立一個立體紋理,如以下示例代碼所示。

LPDIRECT3DVOLUMETEXTURE9 pVolumeTexture;

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

                                &pVolumeTexture );

在渲染圖元之前,把目前紋理設為前面建立的立體紋理。以下示例代碼顯示了渲染一個三角形帶的整個過程。

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

{

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

    d3dDevice->SetTexture( 0, pVolumeTexture );

    d3dDevice->SetFVF( D3DFVF_VOLUMEVERTEX );

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

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

   // 結束繪制。

   d3dDevice->EndScene();

}

繼續閱讀