《Beginning DirectX11 Game Programming》讀書筆記2 一步一步開始第一個DirectX11程序

最好的學習方式就是動手操作。以下代碼可以在書籍官方網站上下載。

 

以下基於Windows7+VS2010 開發環境。並確保您已經安裝DXSDK。

 

開始編寫第一個DirectX程序。

1、運行VS2010,新建項目 BlankWindow

image

 

2、添加Windows創建代碼。此後,當前項目會作爲後續練習項目的模版

 

1、添加main.cpp源文件

2、編寫win32 sdk代碼,程序入口

#include <Windows.h>

int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE prevInstance,
    LPWSTR cmdLine,int cmdShow)
{
    return 0;
}

這裏我們使用wWinMain代替WinMain,支持Unicode參數。對應參數3類型LPWSTR

 

3、初始化窗口、消息處理

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE prevInstance,
    LPWSTR cmdLine,int cmdShow)
{
    UNREFERENCED_PARAMETER(prevInstance);
    UNREFERENCED_PARAMETER(cmdLine);

    // 註冊窗口
    WNDCLASSEX wndClass={0};
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_HREDRAW|CS_VREDRAW;
    wndClass.lpfnWndProc = WndProc;
    wndClass.hInstance = hInstance;
    wndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wndClass.lpszMenuName=NULL;
    wndClass.lpszClassName = "DX11GeoWindowClass";

    if(!RegisterClassEx(&wndClass)){
        return -1;
    }

    // 創建窗口
    RECT rc = {0,0,640,480};
    AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);

    HWND hwnd = CreateWindowA("DX11GeoWindowClass","Blank Win32 Window",
        WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,NULL,NULL,hInstance,NULL);

    if(!hwnd)
        return -1;

    // 顯示
    ShowWindow(hwnd,cmdShow);

    // 消息處理
    MSG msg = {0};
    while(msg.message!=WM_QUIT){
        if(PeekMessage(&msg,0,0,0,PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    switch(message){
    case WM_PAINT:
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd,message,wParam,lParam);
    }

    return 0;
}

Win32 SDK窗口程序基本框架,可以參考MSDN。這裏,UNREFERENCED_PARAMETER用於告訴編譯器不使用的參數,避免編譯器警告提示。

養成處理所有警告提示的良好的編程風格

 

運行效果,

image

 

 

3、新項目 BlankD3DWindow

新建項目到現有解決方案

image

 

進入項目屬性,配置附加鏈接庫。

d3d11.lib;d3dx11.lib;dxerr.lib 注意,半角逗號分隔

image

 

設置鏈接庫引用路徑

$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x86;

image

 

初始化Direct3D

步驟,

  1. 定義創建設備的驅動類型、特性水平、交換鏈
  2. 創建設備類型
  3. 創建渲染目標
  4. 設置視圖

 

創建設備的驅動類型包含,

  • 硬件加速:性能最好,也稱爲HAL(硬件抽象層)
  • WARP :DX11新加入。Windows高級光柵化平臺,採用軟件模擬方式。微軟對指令進行了高度優化
  • 軟件驅動 : 開發者編寫自己的渲染驅動插件。不需要DXSDK支持。但不建議在有高性能要求的程序中使用
  • REFERENCE:軟件模擬所有D3D特性,速度很慢。一般用於開發,需要DXSDK支持
  • NULL:本質上同REFERENCE,但沒有渲染功能

特性水平包含,

  • 11.0
  • 10.1
  • 10.0

 

什麼是交換鏈?

用於交換前後緩衝(可以理解爲前一幀、後一幀,使得連續輸出),包含如下描述屬性

  • 緩衝數量(可以多組)
  • 輸出長寬
  • 緩衝格式
  • FPS。一般液晶顯示器60HZ
定義交換鏈描述。
DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );
    swapChainDesc.BufferCount = 1;//緩衝數量
    swapChainDesc.BufferDesc.Width = width;//輸出長寬
    swapChainDesc.BufferDesc.Height = height;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//格式
    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;//FPS
    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.OutputWindow = hwnd;
    swapChainDesc.Windowed = true;//是否窗口形式 false;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;

 

創建交換鏈,設備上下文,

    unsigned int creationFlags = 0;

#ifdef _DEBUG
    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    HRESULT result;
    unsigned int driver = 0;

    //** 創建device、上下文、交換鏈
    for( driver = 0; driver < totalDriverTypes; ++driver )
    {
        result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver], 0, creationFlags,
            featureLevels, totalFeatureLevels,
            D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,
            &d3dDevice_, &featureLevel_, &d3dContext_ );

        if( SUCCEEDED( result ) )
        {
            driverType_ = driverTypes[driver];
            break;
        }
    }

    if( FAILED( result ) )
    {
        DXTRACE_MSG( "Failed to create the Direct3D device!" );
        return false;
    }
 

創建渲染目標

 
ID3D11Texture2D* backBufferTexture;

    result = swapChain_->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBufferTexture );

    if( FAILED( result ) )
    {
        DXTRACE_MSG( "Failed to get the swap chain back buffer!" );
        return false;
    }

    //** 創建渲染目標
    result = d3dDevice_->CreateRenderTargetView( backBufferTexture, 0, &backBufferTarget_ );

    if( backBufferTexture )
        backBufferTexture->Release( );

    if( FAILED( result ) )
    {
        DXTRACE_MSG( "Failed to create the render target view!" );
        return false;
    }

    d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0 );

設定視角

D3D11_VIEWPORT viewport;
    viewport.Width = static_cast<float>(width);
    viewport.Height = static_cast<float>(height);
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;

    d3dContext_->RSSetViewports( 1, &viewport );
 
 

執行繪圖和呈現

    if( d3dContext_ == 0 )
        return;

    float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };
    d3dContext_->ClearRenderTargetView( backBufferTarget_, clearColor );

    swapChain_->Present( 0, 0 );
 
 

開發電腦硬件,

T430筆記本自帶的NVS5400M顯卡支持DX11,128位存儲器接口,分2G、1G顯存。T410用的是NVS3100M顯卡,不支持DX11。

調試DX11特性,需要支持DX11的顯卡。

image

 

建立模版項目

D3D初始化和銷燬代碼在很多項目中需要用到,爲了避免重複編寫,這裏生成一個類DX11DemoBase。

class Dx11DemoBase
{
public:
    Dx11DemoBase();
    virtual ~Dx11DemoBase();

    bool Initialize( HINSTANCE hInstance, HWND hwnd );
    void Shutdown( );

    virtual bool LoadContent( );
    virtual void UnloadContent( );

    virtual void Update( float dt ) = 0;
    virtual void Render( ) = 0;

protected:
    HINSTANCE hInstance_;
    HWND hwnd_;

    D3D_DRIVER_TYPE driverType_;
    D3D_FEATURE_LEVEL featureLevel_;

    ID3D11Device* d3dDevice_;
    ID3D11DeviceContext* d3dContext_;
    IDXGISwapChain* swapChain_;
    ID3D11RenderTargetView* backBufferTarget_;
};

 

用法,

具體項目實現中,繼承這個類,

#include "dx11demobase.h"
class BlankDemo :
    public Dx11DemoBase
{
public:
    BlankDemo(void);
    virtual ~BlankDemo(void);

    bool LoadContent( );
    void UnloadContent( );

    void Update( float dt );
    void Render( );
};

main.cpp 添加調用

    BlankDemo demo;

    // 初始化DX對象
    bool result = demo.Initialize( hInstance, hwnd );
    if(result==false)
        return -1;

    // 消息處理
    MSG msg = {0};
    while(msg.message!=WM_QUIT){
        if(PeekMessage(&msg,0,0,0,PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        //** DX對象動作
        demo.Update(0.0f);
        demo.Render();
    }

    // 釋放DX對象
    demo.Shutdown();
 

另一種寫法採用std::auto_prt

    std::auto_ptr<Dx11DemoBase> demo( new BlankDemo( ) );

    // 初始化DX對象
    bool result = demo->Initialize( hInstance, hwnd );
    if(result==false)
        return -1;

    // 消息處理
    MSG msg = {0};
    while(msg.message!=WM_QUIT){
        if(PeekMessage(&msg,0,0,0,PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        //** DX對象動作
        demo->Update(0.0f);
        demo->Render();
    }

    // 釋放DX對象
    demo->Shutdown();

std::auto_prt的好處是可以在超出作用域後自動釋放內存。即使異常退出,也會自動釋放。推薦做法。

 

最終運行截圖,

image

 

DX編程錯誤處理,

支持三個方法,

  • TCHAR* DXGetErrorDescription( HRESULT hr ) //得到詳細的錯誤描述
  • TCHAR* DXGetErrorString( HRESULT hr ) //只返回錯誤代碼,沒有描述
  • HRESULT DXTrace( CHAR * strFile, DWORD dwline, HRESULT hr, CHAR *strMsg, BOOL bPopMsgBox )

DXTrace 顯示帶詳細錯誤描述的文本消息,並且帶代碼文件名和出錯行提示。根據不同參數,對應三個宏,

  • DXTRACE_ERR( str, hr )//在調試窗口顯示自定義文本和hr對應的詳細錯誤描述
  • DXTRACE_ERR_MSGBOX( str, hr ) //彈出消息框
  • DXTRACE_MSG( str ) //在調試輸出窗口顯示自定義文本

這些宏在調試DX程序中,很有用。

 

後面將學習2D渲染實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章