1. 程式人生 > >在螢幕繪製一個旋轉的線框立方體(使用頂點快取和索引快取)

在螢幕繪製一個旋轉的線框立方體(使用頂點快取和索引快取)

首先裡面還是有兩個基本的檔案:d3dUtility.h以及d3dUtility.cpp

裡面的主要內容就是實現Direct3D的初始化,以及模板函式的的實現,和訊息迴圈函式的宣告,回撥函式的宣告。

具體的解釋在部落格:在螢幕繪製一個三角形以及Direct3D初始化例程中有詳細的解釋,這裡就不在過多的重複。

我們主要來看主函式檔案Cube.cpp裡面是怎麼實現,我們在程式碼中詳細的講解,下面是cube.cpp內容:

//包含標頭檔案
#include "d3dUtility.h"

//定義一個裝置型別的指標,準備指向裝置
IDirect3DDevice9* Device = 0;

//定義兩個資料,用來規定視窗的大小
const int Width = 640;
const int Height = 480;

//定義兩個指標,一個準備指向頂點快取(VertexBuffer),一個準備指向索引快取(IndexBuffer)
IDirect3DVertexBuffer9* VB = 0;
IDirect3DIndexBuffer9*  IB = 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()
{
    //將我們剛才建立的頂點快取VP進行例項化
	Device->CreateVertexBuffer(
		8 * sizeof(Vertex),    //開闢可以存放8個頂點的快取用來給VP指
		D3DUSAGE_WRITEONLY,    //頂點快取的使用格式,是幾個列舉型別,這個列舉型別表示建立的頂點快取只能寫
		Vertex::FVF,     //將頂點的靈活頂點格式傳進來
		D3DPOOL_MANAGED,    //記憶體池得型別
		&VB,                //定義的頂點快取的指標
		0);                //不使用,文件裡面說是給Window Vista使用

    //將我們剛才建立的索引快取進行IP例項化
	Device->CreateIndexBuffer(
		36 * sizeof(WORD),    //開闢可以存放36個索引的空間給IP指
		D3DUSAGE_WRITEONLY,    //索引快取的屬性,同上面的頂點快取的對應引數
		D3DFMT_INDEX16,        //索引快取的位數,32位和16位,32位有些硬體不支援
		D3DPOOL_MANAGED,       //記憶體池的型別
		&IB,                   //定義的指向索引快取的指標
		0);                    //不使用,文件裡面說是給Window Vista使用


	//用頂點的結構定義一個指標,用來
	Vertex* vertices;
    //對方問的頂點進行上鎖
	VB->Lock(0, 0, (void**)&vertices, 0);

	//對頂點進行賦值,正方形有8個點
	vertices[0] = Vertex(-1.0f, -1.0f, -1.0f);
	vertices[1] = Vertex(-1.0f, 1.0f, -1.0f);
	vertices[2] = Vertex(1.0f, 1.0f, -1.0f);
	vertices[3] = Vertex(1.0f, -1.0f, -1.0f);
	vertices[4] = Vertex(-1.0f, -1.0f, 1.0f);
	vertices[5] = Vertex(-1.0f, 1.0f, 1.0f);
	vertices[6] = Vertex(1.0f, 1.0f, 1.0f);
	vertices[7] = Vertex(1.0f, -1.0f, 1.0f);
    
    //解鎖
	VB->Unlock();

	// 定義多維資料集的三角形
	WORD* indices = 0;
	IB->Lock(0, 0, (void**)&indices, 0);

    //裡面存放的是vertices數組裡面的索引,
	/* 前面要繪製兩個三角形,
    第一個三角形,索引快取0,1,2裡面分別存放的是頂點vertices[0],vertices[1],vertices[2]
    第二個三角形,索引快取3,4,5裡面分別存放的是頂點vertices[0],vertices[2],vertices[3]
    索引快取裡面存放的是頂點快取裡面頂點的索引
    後面同理
    */
	indices[0] = 0; indices[1] = 1; indices[2] = 2;
	indices[3] = 0; indices[4] = 2; indices[5] = 3;

	// 後面
	indices[6] = 4; indices[7] = 6; indices[8] = 5;
	indices[9] = 4; indices[10] = 7; indices[11] = 6;

	// 左面
	indices[12] = 4; indices[13] = 5; indices[14] = 1;
	indices[15] = 4; indices[16] = 1; indices[17] = 0;

	// 右面
	indices[18] = 3; indices[19] = 2; indices[20] = 6;
	indices[21] = 3; indices[22] = 6; indices[23] = 7;

	// 上面
	indices[24] = 1; indices[25] = 5; indices[26] = 6;
	indices[27] = 1; indices[28] = 6; indices[29] = 2;

	// 下面
	indices[30] = 4; indices[31] = 0; indices[32] = 3;
	indices[33] = 4; indices[34] = 3; indices[35] = 7;

	IB->Unlock();


    //攝影機在世界座標中的位置
	D3DXVECTOR3 position(0.0f, 0.0f, -5.0f);
    //被觀察的點在世界座標的位置
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
    //世界座標系中向上方向的向量
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMATRIX V;
    //通過這個介面獲取世界矩陣
	D3DXMatrixLookAtLH(&V, &position, &target, &up);
    
    //設施世界矩陣
	Device->SetTransform(D3DTS_VIEW, &V);

    //用於接收投影矩陣的矩陣
	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(
		&proj,
		D3DX_PI * 0.5f, // 視域體的視域角:90度
		(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<IDirect3DVertexBuffer9*>(VB);
	d3d::Release<IDirect3DIndexBuffer9*>(IB);
}

bool Display(float timeDelta)
{
	if (Device)
	{
        //定義兩個矩陣
		D3DXMATRIX Rx, Ry;

		// 得到一個矩陣Rx:乘這個矩陣就相當於:在X軸上旋轉45度
		D3DXMatrixRotationX(&Rx, 3.14f / 4.0f);

		// 每一幀增加y的旋轉角度
		static float y = 0.0f;
        //得到一個矩陣Ry:乘以這個矩陣就相當於在Y軸旋轉y度,y是一直在變化的
		D3DXMatrixRotationY(&Ry, y);
		y += timeDelta;

		//如果角度大於360度,也就是轉了一圈,就將y軸的旋轉角度重置為0
		if (y >= 6.28f)
			y = 0.0f;

		// 結合矩陣Rx以及Ry,
		D3DXMATRIX p = Rx * Ry;

        //設定世界矩陣
		Device->SetTransform(D3DTS_WORLD, &p);
    
        //清屏
		Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
		Device->BeginScene();
        
        //將頂點快取和資料流連結
		Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
        //設定索引快取
		Device->SetIndices(IB);
        //設定靈活頂點格式
		Device->SetFVF(Vertex::FVF);

		//使用介面畫出來
		Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

		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;

	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE)
			::DestroyWindow(hwnd);
		break;
	}
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//
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畫出來
	d3d::EnterMsgLoop(Display);

	Cleanup();

	Device->Release();

	return 0;
}