我的directX編程學習(4)-座標變換初步

來源:互聯網
上載者:User

這些天對左邊變換比較感興趣。申明,本人對遊戲編程沒興趣,只是近體做的項目用了DirectX而已,用了就要懂麼!況且座標變換也挺用腦的,我的大腦已經很久沒用了,快鏽死了,得動動了。

我的 directX編程學習(4)今天開始看動畫初步,其實主要就是 座標變換。無論對於 directX也好,對於openGL也好, 座標變換都是做基本最重要的部分。所以今天一定要好好學習學習。先看一下D3D的矩陣表示形式:m._11 m._12 m._13 m._14     ux uy uz 0
m._21 m._22 m._23 m._24  =  vx vy vz 0
m._31 m._32 m._33 m._34     wx wy wz 0
m._41 m._42 m._43 m._44     tx ty tz 1下面看一下D3D需要構造的幾個基本矩陣:
1,平移矩陣1  0   0  0
0  1   0  0
0  0   1  0
tx ty tz  1平移矩陣可以由函數:VOID D3DMatrixTranslation(D3DMATRIX* m,FLOAT tx,FLOAT ty,FLOAT tz)來構造。
2,旋轉矩陣
以繞Y軸旋轉為例,矩陣形式如下:cosR 0 -sinR 0
0    1   0   0
sinR 0  cosR 0
0    0   0   1以上矩陣可以通過類似VOID D3DMatrixRotationY(D3DMATRIX* m,FLOAT r)來獲得。同樣可以得到繞X,Z軸旋轉的矩陣。
3,縮放矩陣sx  0  0   0
0   sy 0   0
0   0  sz  0
0   0  0   1可以通過D3DMATRIX* D3DMatrixScaling(D3DMATRIX *pOut,FLOAT sx,FLOAT sy,FLOAT sz)來獲得。得到上述矩陣以後,可以通過矩陣乘法來累積變換,乘法形式同線性代數中所講一樣,不詳述。調用形式如下:D3DMATRIX* D3DMatrixMultiply(D3DMATRIX *pOut,D3DMATRIX *pM1,D3DMATRIX *pM2).其中傳回值同pOut值相同。
在C++中由於利用函數重載,還可以寫成:pOut = pM1 * pM2的形式,當然也可以連乘:pOut = pM1 * pM2 * pM3.
獲得了變換矩陣以後,要對頂點起作用,需要利用m_pDevice->SetTransform(D3DTS_WORLD,&m_matWorld)來設定。當然可以將D3DTS_WORLD換成D3DTS_VIEW,D3DTS_PROJECTION來設定視矩陣和投影矩陣。下面看一個實際應用的例子:
HRESULT CMyD3DApplication::FrameMove()
{
    // rotates the object about the y-axis
 D3DXMATRIX matRotY;
 D3DXMATRIX matRotZ;
 D3DXMatrixRotationY( &matRotY, m_fTime * 0.5f);
 D3DXMatrixRotationZ( &matRotZ, m_fTime * 1.5f);
 
 D3DXMATRIX matTrans;
 D3DXMatrixTranslation(&matTrans, 0.0f, 0.0f, 0.0f); m_pd3dDevice->SetTransform( D3DTS_WORLD, &(matRotY * matRotZ * matTrans));    return S_OK;
}
我們用FrameMove()函數來製作動畫,迴圈播放。我們只需要調用相應函數獲得矩陣,然後乘起來,設定一下就可以了。
再看一小段代碼:
// Each viewport fills a quarter of the window
 m_RViewport.Width = MainViewport.Width / 2;
 m_RViewport.Height = MainViewport.Height / 2;
 m_RViewport.Y = m_SRTViewport.Y = 0;
 m_RViewport.X = m_TRViewport.X = 0;
// Set the full Z range for each viewport
 m_RViewport.MinZ = 0.0f;
 m_RViewport.MaxZ = 1.0f;
m_pd3dDevice->SetViewport(&m_RViewport);
m_matWorld = RotationMatrix1;
m_pd3dDevice->SetTransform(D3DTS_WORLD, &m_matWorld);
m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,
         0,
         0,
         4,  // number of vertices
         0,
         2); // number of primitives
我們只要把已經做好的矩陣設定成當前全局座標矩陣,然後依次繪製圖元就可以了。
從這個例子我們也可以想象出d3d也存在著類似於openGL那樣的一個儲存目前狀態的自動機。
上邊的代碼中我們也看到了使用視口的例子,通過設定不同視口的位置和大小,我們可以在螢幕上顯示出不同的內容,書中就是使用四個視口分別展示了四種不同的變換。

 上次寫blog的時候只是簡單記錄了一下D3D的座標變換,以及讓做好的矩陣應用到情境中的頂點上,並探索了一下使用視口的方法。這次我就把D3D座標變換其餘的部分補充完整。首先介紹一種物體的標記法,然後介紹兩種任意變換的方法,介紹視變換和投影變換,最後介紹深度緩衝的使用。

    上次介紹了對物體在三個座標軸方向上做平移、縮放、旋轉的情況,可以使用D3DX系列的API直接構造變換矩陣,那麼再複雜一點的情況就是繞自身軸的旋轉。使用上述基本變換實際上可以達到這一目的,只是有些麻煩,下面我們來探討一下更為通用的表達方式。為了達到這一目的就必須利用更加複雜的矩陣變換。   首先我們藉助一個結構來描述物體的定位:
struts Object{   D3DMATRIX matLocal;}
用該矩陣中的三個向量分別表示物體的朝向:Look,Up,Right,這個裡的含義如同openGL裡邊相機的Look,Up,Right是一樣的。然而實際上定位一個物體除了上述三個向量表示的姿態以外,還需要一個位置資訊,於是我們用第四行來記錄位置。將該矩陣設定為單位陣表示物體的變換從原點開始,沿座標軸方向。這樣表示以後,物體繞Look軸轉就是橫滾(pitch),繞Up軸轉就是偏航(yaw),繞Right軸轉就是俯仰(roll)。------------------------------------   下面說一下用到的API。將一個向量按照指定矩陣變換的API是:D3DXVectorTransformCoord(D3DXVector* vNew,D3DXVector* vOld,D3DXMatrix* mat),那麼旋轉用的矩陣mat又來源於D3DMatrixRotationAxis(D3DXMatrix* mat,D3DXVector* vAxis,FLOAT fRad),表示繞某一向量旋轉一個角度產生一個變換矩陣。有了這兩個API我們就可以通過將三個姿態向量指定給vAxis和vOld來擷取到新的姿態向量。   需要注意的是,由於計算精度問題,上述計算進行多次以後會存在舍入誤差,使三個姿態向量不再垂直。為瞭解決這個問題需要在旋轉之前對三個向量進行歸一化。這裡的歸一化不是對三個向量各自歸一,而是按照下述方式進行:
D3DXVec3Normalize(&vLook,&vLook);D3DXVec3Cross(&vRight,&vUp,&vLook);D3DXVec3Normalize(&vRight,&vRight);D3DXVec3Cross(&vUp,&vLook,&vRight);D3DXVec3Normalize(&vUp,&vUp);
可以看到是通過向量單獨歸一和叉乘的方式進行,既保證向量歸一,又保證垂直。   matLocal矩陣的儲存形式如下:第一行是Right,第二行是Up,第三行是Look,第四行是Position:
m_pObjects[0].matLocal._11 = vRight.x;m_pObjects[0].matLocal._12 = vRight.y;m_pObjects[0].matLocal._13 = vRight.z;m_pObjects[0].matLocal._21 = vUp.x;m_pObjects[0].matLocal._22 = vUp.y;m_pObjects[0].matLocal._23 = vUp.z;m_pObjects[0].matLocal._31 = vLook.x;m_pObjects[0].matLocal._32 = vLook.y;m_pObjects[0].matLocal._33 = vLook.z;m_pObjects[0].matLocal._41 = vPos.x;m_pObjects[0].matLocal._42 = vPos.y;m_pObjects[0].matLocal._43 = vPos.z;
   下面我們把上述變換過程總結一下:
  1. 確定旋轉角度和旋轉軸。
  2. 取出當前的vRight,vLook,vUp,vPos向量;
  3. 對三個向量進行歸一化;
  4. 利用D3DMatrixRotationAxis(D3DXMatrix* mat,D3DXVector* vAxis,FLOAT fRad)產生旋轉矩陣;
  5. 利用D3DXVectorTransformCoord(D3DXVector* vNew,D3DXVector* vOld,D3DXMatrix* mat)對當前的vRight,vLook,vUp向量進行變換,得到新的vRight,vLook,vUp向量。
  6. 移動位置,獲得新的vPos;
  7. 將新的vRight,vLook,vUp,vPos向量設定到matLocal中。

----------------------------

上邊的表示方法我們看到要7個過程,這略微有些複雜,那麼下面我們來看另外一種簡潔的計算方法-四元數(Quaternion)。

  我們先對比一下實現的差別,然後再具體解釋API的含義。

  1.  確定旋轉角度和旋轉軸。
  2. 利用D3DXQuaternionRotationYawPitchRoll(D3DXMatrix* mat,Float fYaw,FLOAT fPitch,FLOAT fRoll)的到變換矩陣。
  3. 把上述得到的矩陣同matLocal相乘得到新的matLocal;
  4. 做位置的變換。

   四元數的原理有點複雜,由於速成關係我也沒有怎麼看,只是知道可以簡單想象成一個向量加上一次旋轉,具體的運算推導有機會再研究吧。但這個東西用途的確很廣泛,因此被作為一種專門的方法被D3D介紹。

   上邊只用到了一個API,那就是D3DXQuaternionRotationYawPitchRoll(D3DXMatrix* mat,Float fYaw,FLOAT fPitch,FLOAT fRoll),給定繞三個軸的旋轉角度,返回一個變換矩陣。

-----------------------------

   下邊看一下觀察變換,觀察矩陣同物體定位矩陣唯一不同的就是其儲存方式,它採取列向量的儲存方式。相機的各種變換同物體的變換沒有任何不同,最後也是得到一個矩陣,只是D3D提供了一個根據視點位置,相機朝向和向上方向構造矩陣的函數:D3DXMatrixLookAtLH(D3DXMatrix* mat,D3DXVECTOR3* pEye,D3DXVECTOR3* pAt,D3DXVECTOR3* pUp),省著自己算了。最後用m_pd3Device->SetTransform(D3DTS_VIEW,&mat)設定一下就可以了。

   這裡需要注意的是D3DXMatrixLookAtLH()只適合於簡單的頭罩式顯示或者視點跟隨,對於具有複雜旋轉的飛行模擬器這類相機最好還是自己來算。計算的方式同前邊介紹的物體變換的方式一樣,也有兩種方式,一種是複雜的7步變換,一種是簡單的四元數變換。最後將得到的向量按照列向量的形式賦給視矩陣,再利用SetTransform()設定一下就好了。這實際上是一種自己維護相機的方式。

------------------------------

下面看一下投影變換。提到投影就會想到視錐,就會有視域角(FOV-field of view)、寬高比(aspect)和遠近裁減面這四個參數。在D3D裡邊可以利用D3DXMatrixPerspectiveFoVLH(D3DXMATRIX* pOut,FLOAT fovY,FLOAT Aspect,FLOAT zn,FLOAT zf),通過給定的四個參數獲得投影矩陣,然後用m_pd3Device->SetTranform(D3DTS_PROJECTION,&pOut)來設定投影矩陣即可。

-------------------------------   視口的使用上次已經說過了,下面就看一下深度緩衝的使用。   在架構裡邊使用深度緩衝只要讓m_d3dEnumeration.AppUsesDepthBuffer = TRUE.然後在每一幀繪製前用m_pd3Device->Clear()方法清空緩衝區。   HRESULT Clear(                 DWORD Count,//矩形數量                 const D3DRECT *pRects,//矩形指標                 DWORD Flags,//要清除的緩衝類型                 float Z,//Z緩衝設定的值                 DWORD Stencil)//模板緩衝設定的值   書中還講了一個深度緩衝精度影響渲染品質的問題,為了達到無錯誤的穩定效果可以是用W緩衝器。方法如下:   m_pd3Device->SetRenderState(D3DTS_ZNABLE,D3DZB_USEW).但是這需要硬體的支援,為了穩妥起見還是使用Z緩衝比較好。-------------------------------  總結來說,D3D要設定的矩陣分為三種:除了上次提到的全局座標矩陣,其實我理解也就是openGL裡邊對應的模型視圖矩陣中的模型矩陣,還有視矩陣和投影矩陣。它們的設定函數都是pDeviceObject->SetTransform(),只不過參數不同而已。所有的其他函數也好,表示也好最終都是為了獲得這三個矩陣,這讓我們撥開雲霧見太陽,只要心中挂念這矩陣就可以了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.