close



作者Blog︰http://blog.csdn.net/EA/

=======================================

1、MFC框架

  (DX8MFC)

  這裡的MFC框架指的是一個符合遊戲開發應用的框架,當然你也可以寫一個符合你要求的MFC框架。如果你對MF

C比較熟悉的話可以直接從第二章開始閱讀。本框架是以後幾個例子的基礎,如果你對MFC不是很了解的話,就要認

真閱讀本章,以求對這個MFC框架有一個深入的了解。

  框架中包括兩個類︰

  CDX8MFCApp類和CFrameWin類,CDX8MFCApp類是應用程式類,CFrameWin類是框架的主類,以後我們的大部分代

碼都是從這裡擴展的。首先來看一看CDX8MFCApp類,它包括CDX8MFCApp()、ExitInstance()、InitInstance()、On

Idle(LONG lCount)等成員函數和一個Game對象。

  InitInstance()成員函數在程式初始化時就被調用,在這裡我建立了一個窗口︰

  ExitInstance()成員函數在程式終止時被調用,在這裡我們釋放一些對象和指針︰

  OnIdle(LONG lCount)成員函數會在沒有Windows消息要處理的時候被調用,也就是說OnIdle()成員函數會不斷

的被調用,這正好被我們用作遊戲循環。

  BOOL CDX8MFCApp::InitInstance()

  {

   // The one and only window has been initialized, so show and update it.

   m_pMainWnd = new CFrameWin();

   m_pMainWnd->ShowWindow(m_nCmdShow);

   m_pMainWnd->UpdateWindow();

   Game = (CFrameWin*) m_pMainWnd;

   Game->Init();

   return TRUE;

  }

  ExitInstance()成員函數在程式終止時被調用,在這裡我們釋放一些對象和指針︰

  int CDX8MFCApp::ExitInstance()

  {

   // TODO: Add your specialized code here and/or call the base class

   Game->End();

   delete Game;

   return CWinApp::ExitInstance();

  }

  OnIdle(LONG lCount)成員函數會在沒有Windows消息要處理的時候被調用,也就是說OnIdle()成員函數會不斷

的被調用,這正好被我們用作遊戲循環。

  BOOL CDX8MFCApp::OnIdle(LONG lCount)

  {

   // TODO: Add your specialized code here and/or call the base class

   if(Game->window_active==TRUE)

    {

     Game->Active();

     Game->window_active=FALSE;

    }

   Game->Go();

   return TRUE;

  }

  Game對象是一個CFrameWin類指針,我們在InitInstance()成員函數中創建了一個CFrameWin對象並把CFrameWi

n對象的指針值賦給Game。下面我們來看一看CFrameWin類, 它包括Active()、End()、Go()、Init()、Update()等

成員函數。

  Init()成員函數,你可以在這裡做一些自己的初始化。回顧CDX8MFCApp類的InitInstance()成員函數可知,在

完成窗口初始化后InitInstance()成員函數裡就調用了Game->Init(),也就是說Init()在窗口初始化后被調用。

  void CFrameWin::Init()

  {

   AfxMessageBox("Init");

  }

  Go()成員函數會不斷的被循環調用,它又調用了Update()和DestroyWindow()。Update()用于更新窗口,調用D

estroyWindow()則會結束應用程式。如果你把DestroyWindow()語句刪除掉,程式會不斷的循環。

  void CFrameWin::Go() // Game循環

  {

   AfxMessageBox("Go");

   Update();

   DestroyWindow();

  }

  Active()成員函數會在應用程式被擊活的時候被調用。

  void CFrameWin::Active() //窗口被激活

  {

   TRACE("Active");

   AfxMessageBox("Active");

  }

  這個程式並不做任何事,只是一個MFC框架。



2、初始化DirectX

  (DX8MFC1)

  本例將以第一章的MFC框架為基礎對CFrameWin類進行擴展。主要加入了DrawScene()、InitDirect3D(HWND hwn

d)和ShutdownDirect3D()三個函數。

  InitDirect3D(HWND hwnd)函數對Direct3D進行初始化︰

  HRESULT CFrameWin::InitDirect3D(HWND hwnd)

  {

   pID3D = Direct3DCreate8(D3D_SDK_VERSION);

   HRESULT hr;

   do

    {

     // we need the display mode so we can get

     // the properties of our back buffer

     D3DDISPLAYMODE d3ddm;

     hr = pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

     if(FAILED(hr))

      break;

     D3DPRESENT_PARAMETERS present;

     ZeroMemory(&present, sizeof(present));

     present.SwapEffect = D3DSWAPEFFECT_COPY;

     present.Windowed = TRUE;

     present.BackBufferFormat = d3ddm.Format;

     hr = pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,

                  D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice);

     if(FAILED(hr))

      break;

     // we do our own coloring, so disable lighting

     hr = pID3DDevice->SetRenderState(D3DRS_LIGHTING , FALSE);

     }

   while(0);

   return hr;

  }

  IDirect3D是我們首先要用到的界面,你可以這樣寫︰

  IDirect3D8 * pID3D = Direct3Dcreate8(D3D_SDK_VERSION);

  在你使用pID3D以前,請檢查pID3D是否為非空。你下一步通常是創建D3D設備,但在創建D3D設備之前你要調用

GetAdapterDisplayMode方法取得必須的訊息︰

  D3DDISPLAYMODE d3ddm; pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

  接下來是取得當前顯示模式參數。下面的參數是Surface格式。你可以用這些參數來創建一個D3DPRESENT_PARA

METERS架構︰

  D3DPRESENT_PARAMETERS present;

  ZeroMemory(&present, sizeof(present));

  present.SwapEffect = D3DSWAPEFFECT_COPY;

  present.Windowed = TRUE;

  present.BackBufferFormat = d3ddm.Format;

  D3DPRESENT_PARAMETERS描述了顯示器Surface的訊息,交換機製的類型,應用程式是窗口的還是全屏模式等信

息。在本例中, Surface是以拷貝方法代替頁面翻轉的,因為它是一個窗口模式的應用程式。把后台表面設置成與

當前顯示模式相匹配的格式,一個準備顯示的Surface可以Draw在后台表面上。

  現下你可以創建一個IDirect3DDevice8界面了︰

  pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,

            D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice);

  這個函數有六個參數,幸運的是沒有一個是很複雜的。D3DADAPTER_DEFAULT告訴Direct3D使用主顯示器,只有

當你使用多顯示器時才是必須的。你可以使用一個數值來指定另外一個顯示器。調用IDirect3D的GetAdapterCount

將返回系統的適配器數目。

  第二個參數,D3DDEVTYPE_HAL,告訴Direct3D使用硬體加速。其它選項包括D3DDEVTYPE_REF和D3DDEVTYPE_SW,

通常你都會希望使用硬體加速的,但有時侯你可能會使用軟體加速進行測試。指定窗口取得焦點。如果是全屏應用

程式,你需要一個最頂層窗口。 D3DCREATE_SOFTWARE_VERTEXPROCESSING指定頂點處理類型。你也可以使用硬體加

速或是聯合類型,我不使用硬體加速為的是廣泛的兼容性。如果你想支持T&L, 則你必須使用硬體加速。最後兩個

參數很簡單,一個是你以前建立的,而pID3Ddevice是你現下要創建的IDirect3DDevice8界面。如果方法返回D3DER

R_NOTAVAILABLE,則你寫的參數是正確的,但你的設備不支持你指定的參數。

  最完美的是這個方法自動為你創建后台緩沖(back buffers)和深度緩沖(depth buffers) 。剪裁(Clippi

ng)作為后台表面(backface culling)被自動激活。燈光也被自動激活了,直到你定義頂點顏色之前,你可以禁

止使用燈光︰

  pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

  // DrawScene() 函數

  HRESULT CFrameWin::DrawScene()

  {

   HRESULT hr;

   do

    {

     // clear back buffer

     hr = pID3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0,63,0,0), 0, 0);

     if(FAILED(hr))

      break;

     // start drawing

     hr = pID3DDevice->BeginScene();

     if(FAILED(hr))

      break;

     // Put all drawing code here

     hr = pID3DDevice->EndScene();

     if(FAILED(hr))

      break;

     // flip back buffer to front

     hr = pID3DDevice->Present(NULL, NULL, NULL, NULL);

    }

   while(0);

   return hr;

   }

  Clear會填充你指定的緩沖區。你可以填充Z緩沖區、后台緩沖區或摸板緩沖區(stencil buffer)。在這個例

子中你將用綠色填充后台緩沖區。所以,我們設定D3DCLEAR_TARGET標誌和綠色。 在本例中BeginScene和EndScene

並沒有做什麼,但在以後的例子中我們會用到它的。這兩個函數是畫圖元時的例行公事代碼,這個函數不斷的翻轉

后台表面。我們可以不斷的在后台表面畫一些東西,然後把后台表面翻轉到前台表面。

  // ShutdownDirect3D() 函數

  void CFrameWin::ShutdownDirect3D()

  {

   HELPER_RELEASE(pTexture);

   HELPER_RELEASE(pIndexBuffer);

   HELPER_RELEASE(pStreamData);

   HELPER_RELEASE(pID3DDevice);

   HELPER_RELEASE(pID3D);

  }

  ShutdownDirect3D釋放所有的界面。將來你可能要加入額外的代碼來關閉Direct3D界面,但現下已經夠了。如

果你營運程式,你將得到一個綠色背景的窗口。你可以按“ESC”鍵來退出應用程式。



3、畫三角形

  (DX8MFC2)

  定義你的頂點格式,Direct3D引入了一種可變形頂點格式(flexible vertex format)(FVF)的概念。在FVF

中,你定義一個架構其中包括所需要的頂點組成部分。這個架構會隨著你的程式而改變,但在這裡你將初步把它定

義成這個樣子︰

  struct MYVERTEX

  {

   FLOAT x, y, z; // The transformed position

   FLOAT rhw; // 1.0 (reciprocal of homogeneous w)

   DWORD color; // The vertex color

  };

  示例的開始定義了一個頂點架構,頂點的名稱,和每一個三角形的頂點。在你的InitDirect3D函數中,你必須

創建一個頂點緩沖區︰

  int num_elems = sizeof(vertices) / sizeof(vertices[0]);

  pID3DDevice->CreateVertexBuffer(sizeof(MYVERTEX) * num_elems,

                  D3DUSAGE_WRITEONLY,

                  D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pStreamData);

  函數的第一個參數是頂點架構的位元組大小。在應用程式還不能讀取頂點之前,傳一個D3DUSAGE_WRITEONLY標記

給它。這裡可以有不同的標記來指定如何處理你的頂點,但現下你可以確信Direct3D已經能正確的工作了。下一步

,指定我們用的是什麼FVF格式。當你還沒有使用坐標系預轉換之前,指定為D3DFVF_XYZRHW標記。以後你使用自己

的矩陣坐標系轉換時,把它改成D3DFVF_XYZ。D3DFVF_DIFFUSE告訴Direct3D,我們將為每一個頂點指定顏色。

  D3DPOOL_DEFAULT指定內存的管理模式。最後一個參數是頂點緩沖區的指針, 在例子1中你已經定義了它,但

並沒有用上。 如果你不向頂點緩沖區填入有用數據的話,頂點緩沖區是沒有用的︰

  MYVERTEX *v;

  pStreamData->Lock(0, 0, (BYTE**)&v, 0);

  for(int ii = 0; ii < num_elems; ii++)

  {

   v[ii].x = vertices[ii].x;

   v[ii].y = vertices[ii].y;

   v[ii].z = vertices[ii].z;

   v[ii].rhw = vertices[ii].rhw;

   v[ii].color = vertices[ii].color;

  }

  pStreamData->Unlock();

  這是不難理解的,Lock返回一個你想寫入頂點數據的指針。下一步你從你的頂點陣列中拷貝數據。然後,反還

這個指針。這一對的調用可以告訴Direct3D你的FVF格式, 並設定頂點陣列為當前的活動頂點陣列。(你可以有多

個頂點陣列)。

  pID3DDevice->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);

  pID3DDevice->SetStreamSource(0, pStreamData, sizeof(MYVERTEX));

  SetVertexShader告訴Direct3D使用與CreateVertexBuffer同樣的格式。

  SetStreamSource告訴Direct3D使用pStreamData作為當前頂點陣列,並取得所有元素的大小。

  你現下可以加入畫三角形的代碼了。在DrawScene()的BeginScene和EndScene之間加入如下代碼︰

  int num_elems = sizeof(vertices) / sizeof(vertices[0]);

  pID3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, num_elems / 3);

  D3DPT_TRIANGLELIST標記將命令Direct3D畫不連續的三角形。你指定從索引的第0個頂點開始, 指定所要畫的

三角形數目。如果正確的話,你會看到一個三角形畫在先前的綠色背景窗口上。



4、畫索引三角形

  (DX8MFC3)

  上一章的畫三角形模式營運效率是較低的,而實際上我們都會使用DrawIndexedPrimitive()而不是DrawPrimit

ive()。想一想,如果要畫兩個相連的三角形,共有四個頂點。用DrawIndexedPrimitive()畫要畫四個頂點,而用D

rawPrimitive()畫則要畫六個頂點。

  如果你可以頂點建立索引,你就可以用DrawIndexedPrimitive()畫三角形了。我們可以為一個三角形建立這樣

的索引︰

  WORD indices[] = { 0, 1, 2 };

  它表示三角形中,第一個頂點對應于頂點陣列的第0個頂點;三角形中,第二個頂點對應于頂點陣列的第1個頂

點;三角形中,第三個頂點對應于頂點陣列的第2個頂點; 要畫索引三角形,首先要建立索引緩沖︰

  num_elems = sizeof(indices) / sizeof(indices[0]);

  pID3DDevice->CreateIndexBuffer(sizeof(WORD) * num_elems,

                  D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,

                  D3DPOOL_DEFAULT, &pIndexBuffer);

  第二步是用頂點填充這個索引緩沖︰

  WORD *pIndex;

  pIndexBuffer->Lock(0, 0, (BYTE **)&pIndex, 0);

  for(ii = 0; ii < num_elems; ii++)

  {

   pIndex[ii] = indices[ii];

  }

  pIndexBuffer->Unlock();

  設定索引緩沖︰

  pID3DDevice->SetIndices(pIndexBuffer, 0);

  把DrawScene()的相應的pID3DDevice->DrawPrimitive(...)換成︰

  pID3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, sizeof(indices) / sizeof(indices[0]),

                    0, sizeof(indices) / sizeof(indices[0]) / 3);

  營運程式的到的還是一個三角形。



5、加入帖圖

  (DX8MFC4)

  首先,在MYVERTEX架構中加入帖圖坐標系tu和tv,並給頂點陣列賦以適當的值。

  下一步,設置你的帖圖︰

  D3DXCreateTextureFromFile(pID3DDevice, "dx5_logo.bmp", &pTexture);

  pID3DDevice->SetTexture(0, pTexture);

  其中的“dx5_logo.bmp”指的是帖圖文件,你可以用其他的文件代替它,營運程式你會看到一個帶帖圖的三角

形。



6、帖圖立方體

  (DX8MFC5)

  現下樣我們來進入三維的世界吧﹗這裡你要啟用Z緩沖(z-buffer), 設置立方體的材質,世界坐標系和投影坐

標系。啟用Z緩沖(z-buffer),你要在D3DPRESENT_PARAMETERS架構中加入︰

  present.EnableAutoDepthStencil = TRUE;

  present.AutoDepthStencilFormat = D3DFMT_D16;

  這裡告訴DirectX8使用16位的Z緩沖,下一步︰

  pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

  到這裡Z緩沖已經設置完成。最後你還要在DrawScene()中調用清除Z緩沖內容的代碼︰

  pID3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

           D3DCOLOR_RGBA(0,63,0,0), 1.0, 0);

  // MYVERTEX架構改成︰

  struct MYVERTEX

  {

   FLOAT x, y, z; // The transformed position

   DWORD color; // The vertex color

   FLOAT tu, tv; // Texture coordinates

  };

  在InitDirect3D()也作了相應改動,具體可見源代碼。Direct3D中有多種矩陣,在這裡只使用其中的三個︰世

界、視圖和投影矩陣。世界矩陣變換會把正方體放在世界坐標系中,視圖矩陣變換把正方體放在可視空間內,投影

矩陣使正方體看起來有深度感。BuildMatrices()函數將建立這三個矩陣︰

  void CFrameWin::BuildMatrices()

  {

   D3DXMATRIX matrix;

   D3DXMatrixRotationY(&matrix, timeGetTime() / 1000.0f);

   pID3DDevice->SetTransform(D3DTS_WORLD, &matrix);

   D3DXMatrixLookAtLH(&matrix,

             &D3DXVECTOR3(0.0f, 3.0f, -5.0f), // 攝像機的空間位置

             &D3DXVECTOR3(0.0f, 0.0f, 0.0f), // 攝象機觀察點

             &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); // 攝象機向上方向矢量

   pID3DDevice->SetTransform(D3DTS_VIEW, &matrix); // 設置我們的平截面為45度角

   D3DXMatrixPerspectiveFovLH(&matrix, D3DX_PI / 4, 4.0f / 3.0f, 1.0f, 100.0f);

   pID3DDevice->SetTransform(D3DTS_PROJECTION, &matrix);

  }

  營運本章的例子你將看到一個旋轉的正方體。

  你可以從gamedev.363.net下載本文所有例子的源程式,或透過E-Mail向本文作者索取。





作者Blog︰http://blog.csdn.net/EA/














-----
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 eager 的頭像
    eager

    Green Wind

    eager 發表在 痞客邦 留言(0) 人氣()