0. 要學懂3D程式設計,必然要精通3D相關的線性代數、3D幾何、複分析等相關知識,我也因為如此才開始這個部落格系列的寫作,不自己實現,就不是自己的東西,從今天開始,將會把所學的數學知識,從數學推導到代碼實現的心得全部記錄於此。最終得到一個獨立的3D映像引擎,也就完成了對3D映像知識的基本學習。
1. 文章布局。除了本文,所有文章都將由3部分組成。
1) 數學理論推導。
2) 不參看任何樣本,只根據數學原理進行的代碼實現。
3) 項目代碼下載。
2. 語言與開發環境
1) 語言:C/C++。
3D遊戲說白了就倆字:速度。所以對於物件導向這種東西,在速度面前完全可以無視,而且對於數學和圖形庫來說,本來也沒有過多的對象間關係,多是資料結構與函數的關係,所以C++特性我應該不會用很多,有時為了速度還會穿插asm、SIMD和FPU的使用。
2) 開發工具:VS2010。強大到沒得說。
3) 圖形介面:D3D。(DirectX SDK June 2010,:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=3021d52b-514e-41d3-ad02-438a3ba730ba)
“給我顯存地址,我就能創造一個遊戲”。這是從零實現3D映像引擎的目的,也是我認為學會3D編程的必經之路。本來我想使用DirectDraw,完全不使用D3D,而且即便是DirectDraw,也只是用它來擷取顯存地址,其他API全都不用。但是由於現在DirectX SDK已經都沒有ddraw.h了,再使用ddraw是給自己找麻煩,所以我將使用D3D,但只使用其獲得表面地址。因此我將完全拋棄硬體加速,但也反而更可以瞭解硬體加速到底都在做什麼。PS:D3D的初始化的能力更強大,在ddraw時,我們還需要重新調整視窗大小,並且每次寫入像素時,還要考慮到視窗邊框所造成的像素位移,而在D3D的初始化時都已經做好了。
3. 約定與配置
對於這個圖形庫,我們有以下幾個約定:
1) 支援Unicode
2) 視窗程序,方便調試
3) 螢幕色深32BPP
4) 載入的位元影像均為24位位元影像
5) 基於X86系統
需要配置的就只有2個地方:
1) 在VC++ Directories的Include和Lib中加入DirectX SDK相應目錄
2) Linker的Input加入d3d9.lib和d3dx9.lib
4. 項目架構
這是我們要在項目中用到的檔案:
3DConsole:我們實驗的控制台
3DLib:3D相關的函數,也包含了D3D表面的相關控制
Math:數學庫
Diagnosis:診斷庫,用於產生一些日誌
Helper:輔助函數庫,比如讀寫檔案等
Math、Diagnosis、Helper今天沒有用到,空著而已。
3DConsole,要負責建立視窗、系統訊息的迴圈等一般Win32程式的工作,並在其中適當的位置插入Game_Init()、Game_Main()、Game_Shutdown()方法的調用。這些一看代碼便知,這裡另外做了一些計時的工作,以將輸出鎖定到一定的幀數,並且在空閑時也不會總會去做判斷而跑滿CPU。計時器代碼如下:
// 函數定義<br />DWORD GetClock()<br />{<br />return GetTickCount();<br />}</p><p>void StartClock()<br />{<br />g_Clock = GetClock();<br />}</p><p>void WaitClock()<br />{<br />while((GetClock() - g_Clock) < WAIT_TIME)<br />{<br />Sleep(5);<br />}<br />}
3DLib,本文實現了對D3D的初始化,並實現了繪製一個像素的代碼,代碼中有注釋:
#include "CPPYIN.3DLib.h"</p><p>bool _CPPYIN_3DLib::Init3DLib(HINSTANCE hInstance, HWND hWnd, int width, int height)<br />{<br />IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);</p><p>D3DPRESENT_PARAMETERS d3dpp;<br />d3dpp.BackBufferWidth = width;<br />d3dpp.BackBufferHeight = height;<br />d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;<br />d3dpp.BackBufferCount = 1;<br />d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;<br />d3dpp.MultiSampleQuality = 0;<br />d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;<br />d3dpp.hDeviceWindow = hWnd;<br />d3dpp.Windowed = TRUE;<br />d3dpp.EnableAutoDepthStencil = true;<br />d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;<br />d3dpp.Flags = 0;<br />d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;<br />d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;</p><p>d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice);<br />d3d9->Release();</p><p>pDevice->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pSurface, 0);</p><p>return true;<br />}</p><p>int _CPPYIN_3DLib::DrawPixel(int x,int y, DWORD color)<br />{<br />// 建立並初始化鎖定地區<br />D3DLOCKED_RECT lr;<br />memset(&lr, 0, sizeof(lr));</p><p>// 鎖定<br />pSurface->LockRect(&lr,NULL,D3DLOCK_DISCARD);</p><p>// 像素著色<br />DWORD* pBits = (DWORD*)lr.pBits;<br />pBits[x + y * (lr.Pitch >> 2)] = color;</p><p>// 解鎖<br />pSurface->UnlockRect();</p><p>return 1;<br />}</p><p>void _CPPYIN_3DLib::FlipSurface()<br />{<br />// 擷取後台緩衝<br />IDirect3DSurface9* backBuffer = 0;<br />pDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer);</p><p>// 使用自訂表格面填充後台緩衝<br />pDevice->StretchRect(pSurface, 0, backBuffer, 0, D3DTEXF_NONE);</p><p>// GetBackBuffer所得的緩衝需要被釋放,否則會記憶體泄露<br />backBuffer->Release();</p><p>// 將交換鏈結中的後台緩衝顯示<br />pDevice->Present(0, 0, 0, 0);<br />}</p><p>void _CPPYIN_3DLib::Release3DLib()<br />{<br />pSurface->Release();<br />pDevice->Release();<br />}
最後放上架構項目源碼下載:>>點擊進入下載頁<<