電腦遊戲製作開發設計論壇 首頁 電腦遊戲製作開發設計論壇
任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入 

Google
3D遊戲程式設計入門第8章心得-2

 
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫
上一篇主題 :: 下一篇主題  
發表人 內容
yag
Site Admin


註冊時間: 2007-05-02
文章: 688

2673.35 果凍幣

發表發表於: 2010-2-6, PM 6:20 星期六    文章主題: 3D遊戲程式設計入門第8章心得-2 引言回覆

前言:此乃補丁文。只講解心得,不提供完整教學,有興趣的人請自行購買此書。
代碼:
書名:3D遊戲程式設計入門-使用DirectX 9.0實作
作者:Frank D. Luna
譯者:黃聖峰
出版社:博碩文化

用模版(Stencil)畫影子
代碼:
void RenderShadow()
{
   Device->SetRenderState(D3DRS_STENCILENABLE,    true);
    Device->SetRenderState(D3DRS_STENCILFUNC,      D3DCMP_EQUAL);
    Device->SetRenderState(D3DRS_STENCILREF,       0x0);
    Device->SetRenderState(D3DRS_STENCILMASK,      0xffffffff);
    Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
    Device->SetRenderState(D3DRS_STENCILZFAIL,     D3DSTENCILOP_KEEP);
    Device->SetRenderState(D3DRS_STENCILFAIL,      D3DSTENCILOP_KEEP);
    Device->SetRenderState(D3DRS_STENCILPASS,      D3DSTENCILOP_INCR); // increment to 1

   // position shadow
   D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f);
   D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f);

   D3DXMATRIX S;
   D3DXMatrixShadow(
      &S,
      &lightDirection,
      &groundPlane);

   D3DXMATRIX T;
   D3DXMatrixTranslation(
      &T,
      TeapotPosition.x,
      TeapotPosition.y,
      TeapotPosition.z);

   D3DXMATRIX W = T * S;
   Device->SetTransform(D3DTS_WORLD, &W);

   // alpha blend the shadow
   Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
   Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
   Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

   D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::BLACK, d3d::BLACK, d3d::BLACK, d3d::BLACK, 0.0f);
   mtrl.Diffuse.a = 0.5f; // 50% transparency.

   // Disable depth buffer so that z-fighting doesn't occur when we
   // render the shadow on top of the floor.
   Device->SetRenderState(D3DRS_ZENABLE, false);

   Device->SetMaterial(&mtrl);
   Device->SetTexture(0, 0);
   Teapot->DrawSubset(0);

   Device->SetRenderState(D3DRS_ZENABLE, true);
   Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
   Device->SetRenderState(D3DRS_STENCILENABLE,    false);
}

第一行,開啟模版功能,上篇說過了:
代碼:
Device->SetRenderState(D3DRS_STENCILENABLE,    true);

第二行,設定 Ref & Mask == Value & Mask 時通過模版測試,上篇也說過了:
代碼:
Device->SetRenderState(D3DRS_STENCILFUNC,      D3DCMP_EQUAL);

三到五行,設定Ref、Mask、WriteMask,一樣說過了:
代碼:
Device->SetRenderState(D3DRS_STENCILREF,       0x0);
Device->SetRenderState(D3DRS_STENCILMASK,      0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);

六到八行,設定深度測試失敗、模版測試失敗及模版測試成功的反應:
代碼:
Device->SetRenderState(D3DRS_STENCILZFAIL,     D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILFAIL,      D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILPASS,      D3DSTENCILOP_INCR);

前兩個都是Keep不多說,最後一個--當模版測試成功時將模版緩衝器裡的值+1
配合上面將Ref設成0,就可以讓影子不至於在重疊的部份被畫兩遍
因為第一遍畫之前,Ref跟Value都是0,因此通過模版測試,畫上影子
第一遍畫完後,Value被+1,因此變成1
第二遍要畫時,Ref為0,Value為1,因此模版測試失敗,便不再畫上影子,以避免多重混合的現象

跟鏡像一樣,影子本身也是要我們自己計算出來
DX有提供了一個計算影子的函式:D3DXMatrixShadow
需要傳入的是光的方向(lightDirection)與呈現影子的平面(groundPlane):
代碼:
D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f);
D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f);

使用D3DXMatrixShadow函式來計算時,有兩個比較不一般,需要注意的特點:
第一:光不一定要先經過物體才映射到平面上
第二:光的方向向量與平面的法向量間的夾角要小於90度,如果大於90度,則影子不會呈現
而要滿足小於90度這個特點,就不可避免地會造成一個現象 --
光照形成的影子會呈現在物體所在那半面,不一定會呈現在平面的正面(平面的正面朝向即為法向量方向)
像是此範例中,光的方向是從左後上到右前下照射,所以地面的法向量就要是往下的,這樣才能夾角小於90度
但這樣一來,影子就會呈現在平面的背面,因為法向量往下而物體卻在平面上方

另外,我們可以把光的方向跟平面改成:
代碼:
D3DXVECTOR4 lightDirection(-0.707f, 0.707f, -0.707f, 0.0f);
D3DXPLANE groundPlane(0.0f, 1.0f, 0.0f, 0.0f);

如上,剛好反過來,光從右前下照射到左後上
照常理來說,光先經過平面才照射到物體,物體不可能在平面上留下影子
但是D3DXMatrixShadow函式的第一特點允許我們這樣使用
這會得到跟原設定同樣的影子

以上是傳入平行光給D3DXMatrixShadow函式的狀況
書上有說D3DXMatrixShadow也接受點光源,只要將
代碼:
D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f);

改成
代碼:
D3DXVECTOR4 lightPosition(0.707f, -0.707f, 0.707f, 1.0f);

即可
也就是四維向量的最後一維為0.0f時,它是向量,最後一維為1.0f時,它是點(或說是位置座標)
只要設成1.0f,函式自然就會以點光源去計算影子
不過我沒試過點光源的狀況下,以上兩特點是否依然如此,所以請慎用

接著的code是打開混色功能
我們可以從
代碼:
mtrl.Diffuse.a = 0.5f;

看出,質料(Material)的混色透明度(alpha)是以Diffuse屬性的alpha來計算的
然後我們以下面這行來關閉深度測試
代碼:
Device->SetRenderState(D3DRS_ZENABLE, false);

因為接著我們就要把影子畫在平面上
而影子跟平面會具有同樣的深度值
這就造成DX不知道該把影子畫在平面前面還是把平面畫在影子前面
這也就是所謂的z值競爭(z-fighting)
把深度測試關閉後,後畫的東西就會覆蓋過先畫的東西
我們就可以看到完整不閃爍的影子了
回頂端
檢視會員個人資料 發送私人訊息 發送電子郵件
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
可以 在這個版面附加檔案
可以 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作