第三章 虛擬寵物實作
3.3 場景繪製核心
第一人稱視角的移動
遊戲中玩家的視角是採用第一人稱視角,當玩家轉彎時,就要利 用對 Y 軸旋轉的旋轉矩陣作視界轉換。而當玩家移動時,就要利用對 軸、Z 軸位移的位移矩陣作視界轉換,才能改變玩家所看到的畫面。
下面的旋轉矩陣將原來的點(x、y、z)繞 Y 軸旋轉θ角:
下面的位移矩陣將點(x、y、z)移動到新的點(x', y', z'),T 是 位移量:
然後依據使用者的輸入改變視界矩陣:
D3DXMATRIX matView;
if( m_UserInput.bRotateLeft
&& !m_UserInput.bRotateRight ) //往左轉 {
m_fTheta += m_fRotSpeed; //旋轉角度 //建立旋轉矩陣
D3DXMatrixRotationY( &matView,
D3DXToRadian(-m_fRotSpeed));
//作用旋轉矩陣
D3DXVec3TransformNormal(
&m_vecCurLookAt,
&(m_vecCurLookAt-m_vecCurLoc),
&matView );
m_vecCurLookAt += m_vecCurLoc;
}
else if( m_UserInput.bRotateRight
&& !m_UserInput.bRotateLeft ) //往右轉 {
m_fTheta -= m_fRotSpeed; //旋轉角度 //建立旋轉矩陣
D3DXMatrixRotationY( &matView,
D3DXToRadian(m_fRotSpeed) );
//作用旋轉矩陣
D3DXVec3TransformNormal(
&m_vecCurLookAt,
&(m_vecCurLookAt-m_vecCurLoc),
&matView );
m_vecCurLookAt += m_vecCurLoc;
}
if( m_UserInput.bRotateUp
&& !m_UserInput.bRotateDown ) //往前移動 {
//位移
D3DXVECTOR3 offset = D3DXVECTOR3(
m_fMoveSpeed*cosf(D3DXToRadian(m_fTheta)), 0.0f,
m_fMoveSpeed*sinf(D3DXToRadian(m_fTheta)) );
m_vecCurLoc += offset;
m_vecCurLookAt += offset;
}
else if( m_UserInput.bRotateDown
&& !m_UserInput.bRotateUp ) //往後移動
{
D3DXVECTOR3 offset = D3DXVECTOR3(
m_fMoveSpeed*cosf(D3DXToRadian(m_fTheta)), 0.0f,
m_fMoveSpeed*sinf(D3DXToRadian(m_fTheta)) );
m_vecCurLoc -= offset;
m_vecCurLookAt -= offset;
}
//產生視界轉換矩陣
D3DXMatrixLookAtLH( &matView, &m_vecCurLoc,
&m_vecCurLookAt, &m_vecUp );
//設定觀測矩陣
m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
3D 場景建構
在場景的建立方面,為了讓繪製的場景更真實,我們在 3D 物件上 使用貼圖來模擬真實視界所看到的影像及環境。
要使用貼圖首先必須指定貼圖座標。所謂貼圖就是一個 2 維的色 彩值陣列。陣列中的每個元素都有一個唯一的貼圖位址,基本上就是 行和列的位址,分別定義成 u(列)和 v(行)。這個位址稱為貼圖座 標,是以貼圖本身的座標空間來表示的。在貼圖空間中的位址則是相 對於貼圖原點。Direct3D 要求貼圖中的所有貼圖圖素都用統一的位址 範圍,才能把貼圖圖素對應到物件上。為了達成這個目的,Direct3D 使用正規化的位址體系。
struct ThreeDVERTEX {
D3DXVECTOR3 position; //頂點座標 D3DCOLOR color; //色彩 FLOAT tu,tv; //貼圖座標
#define D3DFVF_ThreeDVERTEX (D3DFVF_XYZ|D3DFVF_DIF FUSE|D3DFVF_TEX1)
ThreeDVERTEX* pVertices;
//需先鎖定才能填入模型資料
if( FAILED( hr = m_pVB->Lock( 0, 0,
(BYTE**)&pVertices, 0 ) ) ) return DXTRACE_ERR_NOMSGBOX( "Lock", hr );
// 建立一個地板模型
FLOAT range = m_fRight - m_fLeft; //地板範圍 for( int i=0; i<4; i++ )
{
//一個四方形的地板
pVertices[i].position = D3DXVECTOR3(
(float)pow(-1,i/2)*-range*5.0f, 0.0f,
(float)pow(-1,i+1)*range*5.0f );
pVertices[i].color = 0xffffffff;
pVertices[i].tu = (i/2)*range*2.0f;
pVertices[i].tv = ((i+1)%2)*range*2.0f;
}
// 建立樹木模型
for( i=4; i<8; i++ ) {
pVertices[i].position = D3DXVECTOR3(
0.0f,
(i%2)*3.02f*2.0f,
(float)pow(-1,i/2)*1.33f*2.0f );
pVertices[i].color = 0xffffffff;
pVertices[i].tu = ((i-4)/2)*1.0f;
}
//讀入草地貼圖檔案
D3DXCreateTextureFromFile( pd3dDevice,
"草皮.jpg",
&m_pTexture[0] );
//讀入樹木貼圖檔案
D3DXCreateTextureFromFile( pd3dDevice,
"tree02S.tga",
&m_pTreeTexture //設定世界轉換
D3DXMatrixIdentity( &matWorld );
pd3dDevice->SetTransform( D3DTS_WORLD,
&matWorld );
//指定貼圖
pd3dDevice->SetTexture( 0, m_pTexture[0] );
//繪製地板模型
pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
//設定 ALPHA 混合透明
pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE , TRUE );
//設定 ALPHA 型態
pd3dDevice->SetRenderState( D3DRS_SRCBLEND
, D3DBLEND_SRCALPHA );
pd3dDevice->SetRenderState( D3DRS_DESTBLEND
, D3DBLEND_INVSRCALPHA );
//設定 ALPHA TEST
pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE , TRUE );
//設定 ALPHA 值
pd3dDevice->SetRenderState( D3DRS_ALPHAREF , 0x08 );
, D3DCMP_GREATEREQUAL );
//繪製樹木
pd3dDevice->SetTexture( 0, m_pTreeTexture );
for( int j=0 ; j<40 ; j++ ) {
//讓樹木面向畫面繪製
D3DXMatrixRotationY( &matRotWorld,
-D3DXToRadian( theta ) );
//根據隨機值改變樹木的位置
D3DXMatrixTranslation( &matWorld, TreeLoc[j].x, 0.0f,
TreeLoc[j].z );
D3DXMatrixMultiply( &matWorld,
&matRotWorld,
&matWorld );
//設定世界矩陣
pd3dDevice->SetTransform( D3DTS_WORLD,
&matWorld );
//繪製
pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 8, 2 );
D3DXMatrixIdentity( &matWorld );
pd3dDevice->SetTransform( D3DTS_WORLD,
&matWorld );
}