1. 程式人生 > >在螢幕繪製一個三角形(使用頂點快取)

在螢幕繪製一個三角形(使用頂點快取)

首先標頭檔案d3dUtility.h裡面:

Init3D函式:裡面實現了Direct3D初始化的整個過程:包括獲取介面Direct3D9的指標,檢驗裝置的頂點運算,填充裝置屬性結構體的內容,建立IDirect3DDevice9的介面等。

EnterMsgLoop函式:該函式接受了一個顯示函式的函式指標,然後將應用程式的訊息迴圈進行封裝,穿進去的顯示函式就是進行繪製的函式,然後EnterMsgLoop函式需要知道什麼時候去呼叫這個顯示函式進行顯示。

Release模板和Delete模板:資源銷燬的時候呼叫。

標頭檔案可以在這篇部落格中找到,直接複製就能用。

還有就是d3dUtility.cpp檔案,裡面具體實現了剛才我們在d3dUtility.h檔案中宣告的兩個函式。

同樣也可以在這篇部落格中找到,直接複製拷貝就可以用。

然後在原始檔中我們還需要我們的主檔案,也就是繪製Triangle的檔案:triangle.cpp。我們在程式碼中以註釋的方式進行講解,會比較明瞭一點。

//包含我們剛才建立的d3dUtility.h標頭檔案
#include "d3dUtility.h"

//定義一個用來指向裝置的指標
IDirect3DDevice9* Device = 0;

//設定好我們視窗的寬和高
const int Width = 640;
const int Height = 480;

//定義一個用來頂點快取的指標,用來指向我們的三角形頂點的資料
IDirect3DVertexBuffer9* Triangle = 0; 


//定義一個點的結構,有x,y,z的座標,建構函式,還有靈活頂點格式
struct Vertex
{
	Vertex() {}

	Vertex(float x, float y, float z)
	{
		_x = x;	 _y = y;  _z = z;
	}

	float _x, _y, _z;

	//靈活頂點結構
	static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ;


bool Setup()
{
    //使用介面:CreateVertexBuffer建立頂點快取
	Device->CreateVertexBuffer(
		3 * sizeof(Vertex), // 因為我們只去畫一個三角形,所以只有三個頂點,這裡就給他開闢三個頂點的空間
		D3DUSAGE_WRITEONLY, // 這個標記代表現在建立的快取是:只能寫的靜態快取
		Vertex::FVF,        // 靈活頂點格式
		D3DPOOL_MANAGED,    // 記憶體池的型別
		&Triangle,          // 指向頂點快取的指標的指標
		0);                 // CreatVertexBuffer的最後一個引數,不使用,設定為0,具體參見連結


    //使用頂點結構體定義的頂點指標
	Vertex* vertices;
    //使用之前將要訪問的頂點快取進行上鎖
	Triangle->Lock(0, 0, (void**)&vertices, 0);
    
    //對頂點快取進行賦值操作
	vertices[0] = Vertex(-1.0f, 0.0f, 2.0f);
	vertices[1] = Vertex(0.0f, 1.0f, 2.0f);
	vertices[2] = Vertex(1.0f, 0.0f, 2.0f);
    
    //對頂點快取使用完成之後進行解鎖
	Triangle->Unlock();

    /*正常來說,點設定好之後,經過的變換是:區域性座標系->世界座標系->觀察座標系->背面消隱
->光照->裁剪->投影變換->視口變換->光柵化
    但是現在只有一個2D的三角形,視窗中(也可以理解成世界座標和區域性座標是同一個座標)只有這麼一個圖形,所以直接進行投影變換
    */
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(
		&proj,                        // 要得到的投影矩陣
		D3DX_PI * 0.5f,               // 視域體中的視角
		(float)Width / (float)Height, // 縱橫比,為了減少正方形矩陣對映到矩形螢幕而發生的畸變
		1.0f,                         // 攝影機到近平面的距離
		1000.0f);                     // 攝影機到原平面的距離
    
    //使用剛才我們得到的投影矩陣
	Device->SetTransform(D3DTS_PROJECTION, &proj);

    //設定繪製的狀態
	Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

	return true;
}

void Cleanup()
{
    //使用d3d裡面的Release模板,進行資源的釋放
	d3d::Release<IDirect3DVertexBuffer9*>(Triangle);
}

//顯示韓式
bool Display(float timeDelta)
{
    //如果Device存在或者不為空,則進入判斷
	if (Device)
	{
        //使用此方法進行清除表面
		Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
        //繪製必須是在BeginScene和EndScene之間
		Device->BeginScene();
        
        //將需要繪製的頂點快取和資料流進行連結
		Device->SetStreamSource(0, Triangle, 0, sizeof(Vertex));
        //設定靈活頂點格式
		Device->SetFVF(Vertex::FVF);

		// 繪製三角形
		Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

		Device->EndScene();

        //在裝置擁有的後臺快取區中的顯示下一個快取的內容
		Device->Present(0, 0, 0, 0);
	}
	return true;
}

/*這是一個回撥函式,引數和名字是固定的,代表這個視窗在收到對應的訊息的時候,應該做出什麼動作
現在這個函式裡面只是寫了當按下“ESC”的時候視窗結束
*/
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
    //收到視窗摧毀的訊息的時候退出視窗
	case WM_DESTROY:
		::PostQuitMessage(0);
		break;

    //當按鍵 = ESC 的時候摧毀視窗,併發送一條WM_DESTROY訊息
	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE)
			::DestroyWindow(hwnd);
		break;
	}
    //沒有訊息的時候應該呼叫怎麼做
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
	HINSTANCE prevInstance,
	PSTR cmdLine,
	int showCmd)
{
    //初始化Direct3D
	if (!d3d::InitD3D(hinstance,
		Width, Height, true, D3DDEVTYPE_HAL, &Device))
	{
		::MessageBox(0, "InitD3D() - FAILED", 0, 0);
		return 0;
	}

    //分配資源,現在分配資源過程就是:建立頂點快取,訪問頂點快取,操作頂點快取,
	if (!Setup())
	{
		::MessageBox(0, "Setup() - FAILED", 0, 0);
		return 0;
	}

    //迴圈呼叫傳進去的函式
    //穿進去的函式:Display,就是呼叫:DrawPrimitive進行繪製的過程
	d3d::EnterMsgLoop(Display);

	Cleanup();

	Device->Release();

	return 0;
}

triangle.cpp裡面提到的連結是這兩篇部落格,裡面對函式的使用以及一些相關的概念都有詳細的解釋:

Director3D中的繪製——頂點快取和索引快取

繪製流水線