《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渲染实现。

posted @ 2013-02-15 15:14  5imetro  Views(2631)  Comments(2Edit  收藏  举报