遊戲開發基礎(八)

來源:互聯網
上載者:User

 

第八章
模板緩衝是一個用於獲得某種特效的離屏緩衝
模板緩衝的解析度與後台緩衝和深度緩衝的解析度完全相同,所有模板緩衝中的像素與後台緩衝和深度緩衝中的像素是一一對應的
模板緩衝的功能與模板類似,允許我們動態地,有針對性地決定是否將某個像素寫入後台緩衝中
在Direct3D初始化時,首先必須查詢當前裝置是否支援模板緩衝,如果支援,還必須啟用,為了啟用模板緩衝,必須將繪製狀態D3DRS_STENCILENABLE設為true,如要禁用模板緩衝,則該狀態應設為false
Device->SetRenderState(D3DRS_STENCILENABLE,true);
Device->SetRenderState(D3DRS_STENCILENABLE,false);
DirextX9.0中增加了雙面模板特性,該功能可通過削減繪製陰影體所需要的繪製路徑,從而提升陰影體的繪製速度
IDirect3DDevice9::Clear方法將模板緩衝清空為一個預設值,該方法也可對後台緩衝的深度緩衝進行清空操作
Device->Clear(0,0,D3DCLEAR_TAEGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,0xff000000,1.0f,0);
注意第三個參數增加了D3DCLEAR_STENCIL表明我們要對模板緩衝,目標緩衝(後台緩衝)和深度緩衝進行清空操作,第6個參數用於指定要將模板緩衝清為何值,這裡,我們設為0;

依據nVIDIA報告,在現在硬體中使用模板緩衝可被認為是"沒有計算開銷的"運算,前提是你已經在使用深度緩衝

模板緩衝可與深度緩衝一同建立,為深度緩衝指定格式時,我們可以同時指定模板緩衝格式,實際上,模板緩衝和深度緩衝共用同一個離屏的表面緩衝,而每個像素的記憶體段被劃分為若干部分,分別與某種特定緩衝相對應,考慮3種深度/模板緩衝格式:
#D3DFMT_D24S8  含義是,已建立了一個32位元深度/模板緩衝,其中每個像素的24位指定給深度緩衝,8位指定給模板緩衝
#D3DFMT_D24X4S4 含義是,已建立了一個32位元深度/模板緩衝,其中每個像素的24位指定給深度緩衝,4位指定給模板緩衝,其餘4位不用
#D3DFMT_D15S1 含義是,已建立了一個16位元深度/模板緩衝,其中每個像素的15位指定給深度緩衝,1位指定給模板緩衝
注意,一些格式沒有為模板緩衝分配任何空間,例如,D3DFMT_D32格式僅建立一個32位元深度緩衝
可用模板緩衝來阻止對後台緩衝中某些特定地區進行繪製,判斷是否將某個像素寫入後台緩衝的決策過程稱為模板測試
假定模板已處於啟用狀態,則每個像素都需要進行模板測試,模板測試需要如下兩個運算元:
#左運算元(LHS = ref & mask)該值由應用程式定義的模板參考值(ref)和模板掩碼(mask)通過按位與運算得到
#右運算元(RHS = value & mask)該值由當前進行測試的像素的模板緩衝中的數值(value)與模板掩碼(mask)通過按位與運算得到
模板測試的下一步是依據comparison operation 所指定的比較規則對LHS和RHS進行比較,上述運算式的運算結果為布爾類型(true或false),如果測試為true,將該像素寫入後台緩衝,如果測試為false,將阻止寫入後台緩衝,當然當一個像素不被寫入後台緩衝時,也不會被寫入深度緩衝。
我們可以對模板參考值,模板掩碼,以及對比較函數進行設定

模板參考值(stencil reference value)ref 的預設值為0 ,但我們可用D3DRS_STENCILREF繪製狀態改變該值。

例:將模板參考值設為1
 Device->SetRenderState(D3DRS_STENCILREF,0x01);

模板掩碼(mask)用於屏蔽(隱藏)ref 和value變數中的某些位,其預設值為0xffffffff,表示不屏蔽任何位,我們可藉助繪製狀態D3DRS_STENCILMASK來修改該掩碼值,
例:屏蔽高6位;
 Devie->SetRenderState(D3DRS_STENCILMASK,0x0000ffff);
模板值是當前待測試像素在模板緩衝中的對應值,
例:對第i行,第j列的像素進行測試,則value就是模板緩衝中第i行,第j列的值,我們不能顯式第單獨設定模板值,但可以對模板緩衝進行清空操作,可以用模板的繪製狀態控制將要寫入模板緩衝的內容

可以通過繪製狀態D3DRS_STENCILFUNC來設定比較運算函數,該比較運算函數可取自枚舉類型D3DCMPFUNC
typedef enum D3DCMPFUNC
{
D3DCMP_NEVER = 1,//比較函數總是返回false
D3DCMP_LESS = 2 ,//若LHS < RHS則模板測試成功
D3DCMP_EQUAL = 3,//若LHS = RHS則模板測試成功
D3DCMP_LESSEQUAL = 4,//若LHS <= RHS則模板測試成功
D3DCMP_GREATER = 5,//若LHS > RHS則模板測試成功
D3DCMP_NOTEQUAL = 6,//若(LHS = RHS) == false 則模板測試成功
D3DCMP_GREATEREQUAL = 7,//若LHS >= RHS則模板測試成功
D3DCMP_ALWAYS = 8,//比較函數總是返回true
D3DCMP_FORCE_DWORD = 0x7fffffff,
}D3DCMPFUNC,*LPD3DCMPFUNC;

定義模板緩衝中的值如何進行更新:
#第i行,第j列的像素模板測試失敗,這種情況下,我們可藉助繪製狀態D3DRS_STENCILFAIL將模板緩衝中處於同樣位置的項的更新方式定義如下:Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
#第i行,第j列的像素深度測試失敗,可藉助繪製狀態D3DRS_STENCILZFAIL將模板緩衝中處於同樣位置的項的更新方式:
Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
#第i行,第j列的像素深度測試和模板測試均成功,可藉助繪製狀態D3DRS_STENCILPASS將模板緩衝中處於同樣位置的項的更新方式定義如下:
Device->SetRenderState(D3DRS_STENCILPASS,StencilOperation);
StencilOperation可取一下預定義常量:
D3DSTENCILOP_KEEP 不更新模板緩衝中的值(保留當前值)
D3DSTENCILOP_ZERO 將模板緩衝中的值設為0
D3DSTENCILOP_REPLACE 用模板參考值替代模板緩衝中的對應值
D3DSTENCILOP_INCRSAT 增加模板緩衝中的對應數值,如果超過最大值,取最大值
D3DSTENCILOP_DECRSAT 減小模板緩衝中的對應數值,如果小於最小值,取最小值
D3DSTENCILOP_INVERT  增加模板緩衝中的對應數值,如果超過最大值,則取0
D3DSTENCILOP_DECR    減小模板緩衝中的對應數值,如果如果小於0 ,則取最大值

設定寫掩碼(write mask),該值可屏蔽我們將寫入模板緩衝的任何值的某些位,可用繪製狀態D3DRS_STENCILWRITEMASK來設定寫掩碼的值,其預設值為0xffffffff。
例:對高16位屏蔽:
 Device->SetRenderState(D3DRS_STENCILWRITEMASK,0x0000ffff);

實現鏡面效果,需解決兩個問題:首先,瞭解任意平面物體如何成像,其次,將某一表面地區"標記"為鏡面,然後,就可用只繪製處於鏡面地區中那部分物體的映像了,
如何成像,藉助向量幾何很容易解決,"標記"鏡面藉助模板緩衝來解決
D3DX庫提供了下列函數用於建立相對於任意平面的鏡像變換矩陣
D3DXMATRIX* D3DXMatrixReflect(
D3DXMATRIX* pOut,
CONST D3DXPLANE * pPlane,
)

鏡面效果實現步驟:
(1)像往常那樣繪製整個情境(地板,牆壁和茶壺)但先不繪製茶壺的映像,注意,本步中尚不需修改模板緩衝
(2)將模板緩衝清0
(3)將構成鏡面的圖元僅繪製到模板緩衝中,將模板測試設定為總成功,並指定如果測試通過,模板緩衝值被替換為1,因為我們僅繪製鏡面,
(4)現在我們將茶壺的映像繪製到後台緩衝和模板緩衝中,但要注意,如果通過了模板測試,我們將茶壺的映像僅繪製到後台緩衝中,這次我們設定為如果模板緩衝值為1,模板測試就一定成功,按照這種方式,茶壺僅被繪製到那個在對應的模板緩衝中值為1的地區中,由於在模板緩衝中,只有對應於鏡面地區的部分的模板值為1,所以茶壺的映像將只繪製到鏡面所在的子領域中。

例:
void RenderMirror()
{
 Device->SetRenderState(D3DRS_STENCILENABLE,true);
 Device->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_ALWAYS);//模板比較運算函數設為D3DCMP_ALWAYS,測試總會成功
 Device->SetRenderState(D3DRS_STENCILREF,0x1);
 Device->SetRenderState(D3DRS_STENCILMASK,0xffffffff);
 Device->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
 //如果深度測試失敗,我們模板操作指定為D3DSTENCILOP_KEEP,意味不對模板緩衝中的值進行更新,即保留其當前值,我這樣做,是因為深度測試失敗就意味著當前測試中的像素被遮擋,而我們也不希望將物體映像到某些部分繪製到被遮擋的像素上
 Device->SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);//
 //如果模板測試失敗,我們仍將模板操作指定為 D3DSTENCILOP_KEEP
 Device->SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);
 //如果深度測試和模板測試均通過,我們將模板緩衝的操作方式設定為D3DSTENCILOP_REPLACE,意味用模板參考值(0x1)替代模板緩衝中的對應值
 Device->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_REPLACE);
}

可將繪製狀態D3DRS_ZWRITEENABLE設為false阻止對深度緩衝中進行寫操作
可藉助融合操作並將源融合因子和目標融合因子分別設為D3DBLEND_ZERO和D3DBLEND_ONE來阻止對後台緩衝的更新,將這些融合因子代入融合方程,可推知後台緩衝沒有發生任何變化
FinalPixel = sourePixel * (0,0,0,0) + DestPixel * (1,1,1,1) = (0,0,0,0) + DestPixel = DestPixel

Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ZERO);
Device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);

對繪製狀態進行設定
Device->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);
Device->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_KEEP);
現在我們就設定了一個新的比較操作,然後我們進行模板測試:
(ref & mask) == (value & mask)
(0x1 & 0xffffffff) == (value & 0xffffffff)
(0x1) == (value & 0xffffffff)
這表明僅當value=0x1時模板測試才會成功,由於value僅在模板緩衝中對應於該鏡面的地區中才為0x01,所有僅當我們要在那些地區中進行繪製時,模板測試才會成功,這樣上面的茶壺映像只會被繪製到鏡面中,而不會被繪製到其他表面上
注意,將繪製狀態 D3DRS_STENCILPASS設為了D3DSTENCILOP_KEEP,
意味如果測試成功,則保留模板緩衝中的值,所以,該模板緩衝(模板操作方式為D3DSTENCILOP_KEEP)中的值沒有發生變化。只用模板緩衝來標記那些對應於鏡面地區的像素

為了不遮擋構成鏡面的圖元,我們將深度緩衝清空:
Device->Clear(0,0,D3DCLEAR_ZBUFFEER,0,1.0f,0);
然後我們還要進行融合操作,融合公式:
 FinalPixal = sourcePixel*destPixel + destPixel*(0,0,0,0) = sourcePixel*destPixel
Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);
Device->SetRenderState(D3DRS_DESTLEND,D3DBLEND_ZERO);

當物體在鏡面中成像時,其正面和背面將會相互調換,但繞序並未發生變化,所以,"新"正面的繞序將使Direct3D誤認為它們是背面,類似的"新"背面的繞序會使Direct3D誤認為它們是正面,所以,徐改變背面消隱模式

將融合操作和模板操作禁用,並恢複先前的消隱模式
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,false)
Device->SetRenderState(D3DRS_STENCILENABLE,false)
Device->SetRenderState(D3DRS_CULLIMODE,D3DCULL_CCW);

當給定檢視平面和描述平行光光源(w = 0時)或點光源(w = 1 )的向量時,可以D3DX庫的函數建立陰影矩陣:
D3DXMATRIX * D3DXMatrixShadow(
         D3DXMATRIX* pOut,
         CONST D3DXVECTOR4* pLight,
         CONST D3DXPLANE* pPlane);

藉助模板緩衝,將模板測試設定為只接受第一次得到繪製的那些像素,即在向後台緩衝繪製陰影的像素時,對相應的模板緩衝值進行標記,然後,如果企圖將一個像素寫入已經被繪製(模板緩衝中已被標記的位置)的地區時,模板測試就會失敗,這樣就防治了重疊像素寫入,並由此避免了二次融合的發生。

將模板比較函數設為D3DCMP_EQUAL,將D3DRS_STENCILREF繪製狀態設為0x0,這就指定了如果模板緩衝中對應值為0,陰影就被繪製到後台緩衝中,由於模板緩衝已被清零(0x0),當我們首次將陰影的像素寫入時,模板測試結果為真,但由於已將繪製狀態D3DRS_STENCILPASS設為D3DSTENCILOP_INCR,所以如果試圖對一個已被寫入的像素進行寫操作,模板測試就會失敗,當某像素被首次寫入時,該像素的模板值就會增加為0x1,所以當試圖再次對該像素進行寫操作時,模板測試就會失敗,這樣就避免了對一個像素多次寫入,從而避免了二次融合的發生

模板緩衝和深度緩衝共用同一個表面儲存區,所以二者是同時建立的,我們可用D3DFORMAT類型指定深度/模板表面的類型

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.