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

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

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


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

2673.35 果凍幣

發表發表於: 2010-2-24, AM 12:36 星期三    文章主題: 3D遊戲程式設計入門第13章心得-1 引言回覆

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

Camera類別
這個範例也有camera
walk函式及strafe函式請依照第12章的心得所述來修改
另外因為這個範例中,會固定把camera設在地表上5單位處
所以fly函式沒有LANDOBJECT型態的處理

在此補充一下一個第12章心得忘了說的東西
關於camera三視軸旋轉的正向與反向的判定
因為dx預設是左手座標系,所以是以左手姆指定律來判斷的
只要將左手姆指指向跟camera的右軸的方向平行
那麼另外四指往掌內彎曲的旋轉方向即為視軸旋轉的正向
因此,pitch時往下轉即為正,往上轉即為負
yaw時往右轉即為正,往左轉即為負
roll時往左轉即為正,往右轉即為負

D3D命名空間
代碼:
float GetRandomFloat(float lowBound, float highBound);

此函式的註解說其範圍為[lowBound, highBound]
但實際上我們看其實作方式會發現
代碼:
float f = (rand() % 10000) * 0.0001f;

這行取得的範圍並不像它註解說的是[0, 1]而是[0, 0.9999]才對
也因此經過計算後,真正的範圍該是[lowBound, highBound)
你永遠不可能得到一個隨機數 == highBound
這是要注意的地方

新增的Lerp函式為
代碼:
float d3d::Lerp(float a, float b, float t)
{
   return a - (a*t) + (b*t);
}

但是將它改成
代碼:
float d3d::Lerp(float a, float b, float t)
{
   return a + ( b - a ) * t;
}

這樣較好
一是因為只做一次乘法應該比較快
二是因為這種寫法比較符合其原意

Lerp就是線性內插,所謂的線性內插,就是按比例取個值而已
比如說,A車在高速公路上以固定速度移動
它從標明30公里處開始,移到80公里處,花了30分鐘
請問它開始移動15分鐘後的位置是在幾公里處?
此題a為30,b為80,t就是15/30,所以可得答案為55公里處
簡單來說,15分鐘是30分鐘的一半,而30公里到80公里是50公里遠
按比例,取一半就是25公里,因為是從標明30公里處開始的,所以答案就是55公里處
應該很好理解吧?公式的意思就是 初值+總量*比例

FPSCounter類別
_fpsString屬性需要先初始化
不然render時在第一秒內它的值會是亂七八糟的字串
雖然不會顯示出來,但總是一個隱患
請在FPSCounter::FPSCounter(IDirect3DDevice9* device)建構子的最下面加上:
代碼:
ZeroMemory( _fpsString, sizeof( _fpsString ) );
_fpsString[0] = '0';

這會讓它在第一秒內顯示出0

Terrain類別
這個類別要改的就多了
首先在terrain.h檔中,請把
代碼:
int  getHeightmapEntry(int row, int col);

改成
代碼:
float getHeightmapEntry(int row, int col);


代碼:
void setHeightmapEntry(int row, int col, int value);

改成
代碼:
void setHeightmapEntry(int row, int col, float value);


代碼:
std::vector<int> _heightmap;

改成
代碼:
std::vector<float> _heightmap;


然後在terrain.cpp檔中,請把
代碼:
int Terrain::getHeightmapEntry(int row, int col)
{
   return _heightmap[row * _numVertsPerRow + col];
}

改成
代碼:
float Terrain::getHeightmapEntry(int row, int col)
{
   if( row >= _numVertsPerRow || row < 0 || col >= _numVertsPerCol || col < 0 )
      return 0.0f;

   return _heightmap[row * _numVertsPerRow + col];
}

以此程式結構來說,_heightmap不一定都會是整數
所以把_heightmap相關的,都從int類型改成float類型會是比較合理的作法
我不太清楚為什麼作者會把它設成int類型,可能只是為了省事吧

另外getHeightmapEntry函式必須要判斷參數的範圍
否則執行時如果走出邊界外,就會造成fatal error,導致程式被中斷
因此設值時也要做相對應的檢查:
代碼:
void Terrain::setHeightmapEntry(int row, int col, int value)
{
   _heightmap[row * _numVertsPerRow + col] = value;
}

改成
代碼:
void Terrain::setHeightmapEntry(int row, int col, float value)
{
   if( row >= _numVertsPerRow || row < 0 || col >= _numVertsPerCol || col < 0 )
      return;

   _heightmap[row * _numVertsPerRow + col] = value;
}


接著在computeVertices函式中
有個很嚴重的錯誤
我們先把要改的地方看一下後再來講解:
代碼:
// coordinates to start generating vertices at
int startX = -_width / 2;
int startZ =  _depth / 2;

// coordinates to end generating vertices at
int endX =  _width / 2;
int endZ = -_depth / 2;

改成
代碼:
// coordinates to start generating vertices at
int startX = -_width;
int startZ =  _depth;

// coordinates to end generating vertices at
int endX =  _width;
int endZ = -_depth;


代碼:
for(int z = startZ; z >= endZ; z -= _cellSpacing)
{
   int j = 0;
   for(int x = startX; x <= endX; x += _cellSpacing)
   {
      // compute the correct index into the vertex buffer and heightmap
      // based on where we are in the nested loop.
      int index = i * _numVertsPerRow + j;

      v[index] = TerrainVertex(
         (float)x,
         (float)_heightmap[index],
         (float)z,
         (float)j * uCoordIncrementSize,
         (float)i * vCoordIncrementSize);

      j++; // next column
   }
   i++; // next row
}

改成
代碼:
for(int z = startZ; z >= endZ; z -= _cellSpacing * 2)
{
   int j = 0;
   for(int x = startX; x <= endX; x += _cellSpacing * 2)
   {
      // compute the correct index into the vertex buffer and heightmap
      // based on where we are in the nested loop.
      int index = i * _numVertsPerRow + j;

      v[index] = TerrainVertex(
         x / 2.0f,
         _heightmap[index],
         z / 2.0f,
         j * uCoordIncrementSize,
         i * vCoordIncrementSize);

      j++; // next column
   }
   i++; // next row
}

從上面可以看出來,我把用來當成for迴圈iterator的變數都乘以了2
為什麼要這樣做呢?因為
代碼:
int startX = -_width / 2;

這種方式在_width不為偶數時,會導致整個地圖都崩潰掉
當_width為奇數時,除以2後應該存在的0.5存放到int中就消失了
雖然原範例中以傳入cellSpacing為10的方式讓_width一定可以被2整除
但這純粹是個案,如果傳入5的話,地圖就崩潰掉了(因為_numCellsPerRow跟_numCellsPerCol都是63)
如果單純把startX等變數改成float型態,因為它們是要拿來做迴圈初值與終值
可能會造成迴圈次數的不確定性,也不適合
所以最好的方式就是把它們乘以2,等要存入VertexBuffer時再除以2.0f就好了

至於bool Terrain::genTexture(D3DXVECTOR3* directionToLight)函式中最後面的
代碼:
hr = D3DXFilterTexture(
   _tex,
   0, // default palette
   0, // use top level as source level
   D3DX_DEFAULT); // default filter

這是拿來產生較低mipmap等級的材質用的
雖然書中有說,但範例中沒有註解標明,所以稍提一下

另外書上對灰階地形高度圖(raw檔)做舉例時,是以256x256的大小在舉例
而範例中的註解是以128x128的大小在舉例
可實際上書附的raw檔的大小只有64x64
不要被那些不明的舉例給騙了…

terrainDriver.cpp
bool Display(float timeDelta)函式中
一樣是把W鍵跟S鍵的方向倒過來:
代碼:
if( ::GetAsyncKeyState('W') & 0x8000f )
   TheCamera.pitch(1.0f * timeDelta);

if( ::GetAsyncKeyState('S') & 0x8000f )
   TheCamera.pitch(-1.0f * timeDelta);

改成
代碼:
if( ::GetAsyncKeyState('W') & 0x8000f )
   TheCamera.pitch(-1.0f * timeDelta);

if( ::GetAsyncKeyState('S') & 0x8000f )
   TheCamera.pitch(1.0f * timeDelta);

這樣操作pitch比較符合直覺
回頂端
檢視會員個人資料 發送私人訊息 發送電子郵件
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

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


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