1. 程式人生 > >一個DrectX程式的基本流程(DXD10以下)

一個DrectX程式的基本流程(DXD10以下)

本文章通過一個簡單的示例來說明一下一個D3D程式的基本流程

[注]:本程式使用DirectX 9.0實現並使用了Qt,參照的時候不要連結庫連結錯了哦.

示例<使用D3D渲染一個jpg影象到視窗>

步驟:
1、建立一個Windows視窗程式
2、初始化D3D
3、渲染

1、使用VS2010(或其他版本)建立一個標準的WIN32程式.  (這個步驟不多做解釋)


2、以下步驟的操作封裝在D3DRender中

步驟2、3:

封裝一個類(D3DRender)

test.h


#include "d3dx9.h"
#include "Windows.h"
#include <QImage>

class D3DRender
{
private:
    static int                     g_DeviceWidth     ;
    static int                     g_DeviceHeight    ;
    static LPDIRECT3D9             g_pD3D            ; //* 用來建立D3D裝置
    static LPDIRECT3DDEVICE9       g_pd3dDevice      ; //* D3D裝置
    static LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer   ; //* 頂點快取
    static LPDIRECT3DTEXTURE9      g_pTexture        ; //* 紋理
    static HWND                    g_hTargetWin      ; //* 目標視窗(也就是該D3D程式的主視窗)
    static QImage                  g_TargetImage     ; //* 被渲染的圖片

    //響應操作
    void onKeyDown(WPARAM wParam, LPARAM lParam);
    void onPaint(HDC hdc);
    void getWndClassEx(WNDCLASSEX& wc);

public:
    D3DRender();
    ~D3DRender();

    HRESULT initD3D(HWND hWnd);
    void render();

    void cleanup();
    void renderTest();
};

test.cpp

#include "test.h"

#include "atldef.h"

// .lib files.
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

int                     D3DRender::g_DeviceWidth     = 0;
int                     D3DRender::g_DeviceHeight    = 0;
LPDIRECT3D9             D3DRender::g_pD3D            = 0; //* 用來建立D3D裝置
LPDIRECT3DDEVICE9       D3DRender::g_pd3dDevice      = 0; //* D3D裝置
LPDIRECT3DVERTEXBUFFER9 D3DRender::g_pVertexBuffer   = 0; //* 頂點快取
LPDIRECT3DTEXTURE9      D3DRender::g_pTexture        = 0; //* 紋理
HWND                    D3DRender::g_hTargetWin      = 0; //* 目標視窗(也就是該D3D程式的主視窗)
QImage                  D3DRender::g_TargetImage        ; //* 被渲染的圖片

//* 帶RHW 和 Color格式的頂點
struct CustomVertex_RHW_COLOR
{
    float x,y,z,rhw;
    unsigned long color;
};

#define D3DFVF_CUSTOMERTEX_RHW_COLOR (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

//* 帶TEX1格式的頂點
struct CustomVertex_XYZ_TEX1
{
	float x,y,z;
	float tu,tv;
};

#define D3DFVF_CUSTOMERTEX_XYZ_TEX1 (D3DFVF_XYZ|D3DFVF_TEX1)

void D3DRender::cleanup()
{
    if( g_pVertexBuffer != NULL)
        g_pVertexBuffer->Release();
    if( g_pd3dDevice != NULL)
        g_pd3dDevice->Release();
    if( g_pD3D != NULL)
        g_pD3D->Release();
}

void D3DRender::renderTest()
{
    if( NULL == g_pd3dDevice )
            return;

	float fBlue = 0.01024;

	//* 初始化背景顏色
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
		D3DCOLOR_COLORVALUE(0.0f, 0.0f, fBlue, 1.0f), 1.0f, 0); 

	//* 開始渲染場景
	g_pd3dDevice->BeginScene();

	//* 獲取需要渲染的緩衝區
	LPDIRECT3DSURFACE9 pBackBuffer;
	g_pd3dDevice->GetRenderTarget(0, &pBackBuffer);

	//* 如果紋理被建立則將紋理渲染
	if(g_pTexture)
	{
		//* 渲染到紋理
		LPDIRECT3DSURFACE9 pRenderSurface;
		g_pTexture->GetSurfaceLevel(0, &pRenderSurface);

		RECT wrt = {0};
		GetWindowRect(g_hTargetWin, &wrt);
		
		float currentWindowWidth = wrt.right - wrt.left;
		float currentWindowHeight = wrt.bottom - wrt.top;
		float currentImageWidth = g_TargetImage.width();
		float currentImaeHeight = g_TargetImage.height();

		float showImageWidth  =  currentImageWidth ;
		float showImageHeight =  currentImaeHeight;


		//D3DVIEWPORT9 viewPort = {0,0, showImageWidth / aspX, showImageHeight / aspY, 0.0f, 1.0f};
		//g_pd3dDevice->SetViewport(&viewPort);


		//* 將圖片資料拷貝到紋理
		D3DLOCKED_RECT lockRect;
		if(SUCCEEDED(pRenderSurface->LockRect(&lockRect, 0, 0)))
		{
			unsigned char* pdata = g_TargetImage.bits();
			unsigned char* pDstBits = (unsigned char*)lockRect.pBits;
			int byteCount = g_TargetImage.byteCount();

			memcpy(pDstBits, pdata, byteCount);
			pRenderSurface->UnlockRect();
		}

		// Make sure that the z-buffer and lighting are disabled
		g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
		g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

		// Use the alpha channel
		g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
		g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

		// Draw our quad
		g_pd3dDevice->SetTexture( 0, g_pTexture );
		g_pd3dDevice->SetStreamSource(0, g_pVertexBuffer, 0 ,sizeof(CustomVertex_XYZ_TEX1));
		g_pd3dDevice->SetFVF(D3DFVF_CUSTOMERTEX_XYZ_TEX1);

		// Render target
		g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); 
	}

	//* 結束場景渲染
	g_pd3dDevice->EndScene();
	
	//* 重新整理
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

HRESULT D3DRender::initD3D(HWND hWnd)
{
	g_DeviceWidth  	   = 0;
	g_DeviceHeight 	   = 0;
	g_pD3D         	   = 0;
	g_pd3dDevice   	   = 0;
	g_pVertexBuffer	   = 0;
	g_pTexture     	   = 0;

	g_hTargetWin       = hWnd;


	//* 載入需要渲染的圖片 
	if(!g_TargetImage.load("D:/SVN/code/output/W3L/res/Image/Common/Test/GameZoneAD.png"))
	{
		return E_FAIL;
	}

	//* 根據需要轉換格式
	//g_TargetImage = g_TargetImage.convertToFormat(QImage::Format_RGBA8888_Premultiplied);

    BOOL isWindowMode = TRUE;
	HRESULT hr = E_FAIL;

    // 建立D3D物件
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;
		
    // 獲取當前的顯示模式
    D3DDISPLAYMODE d3ddm;

	hr = g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );
    if(FAILED(hr))
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.Windowed = isWindowMode; // 是否全屏模式
	//if(isWindowMode)
	//{
	//	RECT rt;
	//	GetWindowRect(hWnd,&rt);
	//    d3dpp.BackBufferWidth  = rt.right - rt.left ;
	//    d3dpp.BackBufferHeight = rt.bottom - rt.top ;
	//}
	//else
	//{
	//	d3dpp.BackBufferWidth  = d3ddm.Width ;
	//	d3dpp.BackBufferHeight = d3ddm.Height;
	//}
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   // 設定交換模式
    d3dpp.BackBufferFormat = d3ddm.Format;      // 設定背景緩衝區格式為當前左面格式
    d3dpp.BackBufferCount = 1;


    // 建立D3D裝置
    // 第一個引數:使用預設的顯示卡
    // 第二個引數:請求使用硬體抽象層(HAL)
    // 第三個引數:視窗控制代碼
    // 第四個引數:使用軟體處理頂點
    // 第五個引數:建立的引數
    // 第六個引數:建立的D3D裝置指標
	hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&g_pd3dDevice );

    if(FAILED(hr))
    {
        return E_FAIL;
    }

    //初始化頂點緩衝區
    int w = isWindowMode ? d3ddm.Width  : d3dpp.BackBufferWidth;
    int h = isWindowMode ? d3ddm.Height : d3dpp.BackBufferHeight;
	g_DeviceWidth  = w;
	g_DeviceHeight = h;


	//* 注意0~1 是寬度1是實際寬度,0是0,大於1就是放大了
	CustomVertex_XYZ_TEX1 vertexs[] =
	{
		{-1.0f,  1.0f, 0.0f, 0.0f, 0.0f},
		{ 1.0f,  1.0f, 0.0f, 1.0f, 0.0f},
		{-1.0f, -1.0f, 0.0f, 0.0f, 1.0f},
		{ 1.0f, -1.0f, 0.0f, 1.0f, 1.0f},
	};
    
    int vertexsSize = sizeof(vertexs);

	//* 建立定點快取
    LPDIRECT3DVERTEXBUFFER9 pVertexBuffer;
	hr = g_pd3dDevice->CreateVertexBuffer(vertexsSize,
		                                 D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMERTEX_XYZ_TEX1,
		                                 D3DPOOL_DEFAULT, &pVertexBuffer, NULL);
    if(FAILED(hr))
    {
        return E_FAIL;
    }
    g_pVertexBuffer = pVertexBuffer;

    void* pVertex = 0;
    pVertexBuffer->Lock(0, vertexsSize, (void**)&pVertex, 0);
    memcpy(pVertex, vertexs, vertexsSize);
    pVertexBuffer->Unlock();


	//* 初始化紋理
	hr = g_pd3dDevice->CreateTexture(g_TargetImage.width(),
								     g_TargetImage.height(),
									 1,
									 0,
									 d3ddm.Format,
									 D3DPOOL_MANAGED,
									 &g_pTexture,
									 NULL);
	if(FAILED(hr))
	{
		return E_FAIL;
	}
	

    return S_OK;
}

D3DRender::D3DRender()
{
	
}

D3DRender::~D3DRender()
{
	cleanup();
}

void D3DRender::render()
{
    if( NULL == g_pd3dDevice )
            return;


	renderTest();
	return ;
}

這個D3DRender最好是最為全域性的單例使用。

在WM_CREATE的處理函式中:

D3DRender::Instance()->initD3D(hWnd);

在WM_PAINT的處理函式中

D3DRender::Instance()->render();

執行程式,得到結果: