【寒江雪】DX11初始化讲解

DX11初始化

  今天来介绍Direct3D-11的初始化方法。初始化Direct3D-11与初始化Direct3D-9有点不同。总体来看,初始化Direct3D-11分为以下几个步骤

  • 描述交换链
  • 创建D3D设备
  • 创建设备上下文接口
  • 创建交换链接口
  • 创建目标渲染视图
  • 创建深度模板缓存视图(可选)
  • 设置视口

描述交换链

  首先来介绍什么是交换链。所谓交换链就是一个链表,在链表上的每一个节点用来描述每一帧画面,每一次绘制的时候都会根据链表上的指定节点来绘制当前画面。这是链的描述。
  至于交换呢,就表明这个链表是不断地在循环的,需要马上绘制的节点可以称之为前台,就好像聚光灯下的舞台,马上就要上演节目。而以后要绘制到窗口的节点就是后台,就好像是下一个节目。
  通过交换链不断地循环,D3D把每一帧画面保存到后台中,并不断地把后台缓存中渲染好的画面呈现到窗口上。

  交换链通过一个结构体来描述。要描述交换链,只需要填充这个结构体即可。

typedef struct DXGI_SWAP_CHAIN_DESC
{
    DXGI_MODE_DESC BufferDesc;
    DXGI_SAMPLE_DESC SampleDesc;
    DXGI_USAGE BufferUsage;
    UINT BufferCount;
    HWND OutputWindow;
    BOOL Windowed;
    DXGI_SWAP_EFFECT SwapEffect;
    UINT Flags;
}DXGI_SWAP_CHAIN_DESC;

其中的成分如下:

  • DXGI_MODE_DESC BufferDesc:描述后台缓冲区的结构体。用来描述显示模式。
  • DXGI_SAMPLE_DESC SampleDesc:描述多重采样的参数。
  • DXGI_USAGE BufferUsage:一种枚举类型,用于描述CPU对后台缓冲区的用法。后台缓存可以用作着色器的输入或者渲染目标的输出。
  • UINT BufferCount:描述交换链中后台缓存的数量,通常设为1
  • HWND OutputWindow:指定输出窗口句柄
  • BOOL Windowed:描述是否是全屏显示。
  • DXGI_SWAP_EFFECT SwapEffect:用于描述前台缓存在绘制之后D3D应该作何选择。
  • UINT Flags:用于描述交换链的行为。设为0即可。

对于DXGI_MODE_DESC结构体,其成分如下

typedef struct DXGI_MODE_DESC
{
    UINT Width;
    UINT Height;
    DXGI_RATIONAL RefreshRate;
    DXGI_FORMAT Format;
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
    DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC;
  • UINT Width:描述窗口分辨率宽度。
  • UINT Height:描述窗口分辨率高度。
  • DXGI_RATIONAL RefreshRate:用于描述刷新频率的结构体。
  • DXGI_FORMAT Format:用于描述显示格式的枚举类型。设置为DXGI_FORMAT_R8G8B8A8_UNORM即可
  • DXGI_MODE_SCANLINE_ORDER ScanlineOrdering:用于描述扫描线绘制模式的枚举。可以设置为0
  • DXGI_ODE_SCALING Scaling:用于描述缩放形式的枚举类型,默认使用DXGI_MODE_SCALING_UNSPECIFIED。

这里涉及到的DXGI_RATIONAL结构体。成分如下

typedef struct DXGI_RATIONAL
{
    UINT Numerator;
    UINT Denominator;
} DXGI_RATIONAL;
  • UINT 类型的Numerator:表示刷新频率的分子
  • UINT 类型的Denominator:表示刷新频率的分母
  • Numerator=60,Denominator=1表示每秒刷新6次

现在我们回到DXGI_SWAP_CHAIN_DESC中,其中的DXGI_SAMPLE_DESC。

typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;
    UINT Quality;
} DXGI_SAMPLE_DESC;
  • UINT类型的Count:描述每一个像素多重采样的次数。
  • UINT类型的Quality:描述图像品质等级。有效范围是0~1,小于CheckMultisampleQualityLevels的返回值。
  • 默认值为Count=1,Quality=0;

对以上进行总结消化之后,就可以来描述我们需要的交换链了。

DXGI_SWAP_CHAIN_DESC scDesc;
        ::ZeroMemory(&scDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
        scDesc.OutputWindow = hwnd;     //描述输出窗口
        scDesc.BufferCount = 1;//缓存数量
        scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//描述后台缓存格式
        scDesc.BufferDesc.Width = WINDOW_WIDTH; //描述后台缓存分辨率宽度
        scDesc.BufferDesc.Height = WINDOW_HEIGHT;//描述后台缓存分辨率高度
        scDesc.BufferDesc.RefreshRate.Denominator = 1;//刷新频率的分母
        scDesc.BufferDesc.RefreshRate.Numerator = 60;//刷新频率的分子,这两项表明每秒刷新6次
        scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
        scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        scDesc.Flags = 0;
        scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        scDesc.SampleDesc.Count = 1;    //这里不使用多重采样,因此数量设为1
        scDesc.SampleDesc.Quality = 0;  //不使用多重采样,设为0
        scDesc.Windowed = true;         //设置为窗口模式

描述目标功能级别

  交换链描述完成之后,还需要描述目标功能级别,告诉Direct3D使用哪一套Shader.它由一个D3D_FEATURE_LEVEL枚举类型描述,其中包含的内容如下:

typedef enum D3D_FEATURE_LEVEL {
  D3D_FEATURE_LEVEL_9_1   = 0x9100,
  D3D_FEATURE_LEVEL_9_2   = 0x9200,
  D3D_FEATURE_LEVEL_9_3   = 0x9300,
  D3D_FEATURE_LEVEL_10_0  = 0xa000,
  D3D_FEATURE_LEVEL_10_1  = 0xa100,
  D3D_FEATURE_LEVEL_11_0  = 0xb000,
  D3D_FEATURE_LEVEL_11_1  = 0xb100,
  D3D_FEATURE_LEVEL_12_0  = 0xc000,
  D3D_FEATURE_LEVEL_12_1  = 0xc100
} D3D_FEATURE_LEVEL;

  其具体含义可以去查阅MSDN,我们可以这么用如下:

D3D_FEATURE_LEVEL featureLevels[] = {
        D3D_FEATURE_LEVEL_11_0,//D3D11所支持的特征,包括shader model 5
        D3D_FEATURE_LEVEL_10_1,//D3D10.1所支持的特征,包括shader model 4
        D3D_FEATURE_LEVEL_10_0,//D3D10.0所支持的特征,包括shader model 4
};

创建设备和交换链以及设备上下文

  经过层层铺垫,终于来到了这最关键的一步了。那就是通过D3D11CreateDeviceAndSwapChain创建Direct3D设备接口以及交换链。Direct3D设备接口由ID3D11Device定义,它用于创建资源。而设备上下文接口由ID3D11DeviceContext定义,它代表设备上下文生成渲染指令。交换链由IDXGISwapChain定义,之前已经介绍过了,这里就不再重复。
  在MSDN上能查到D3D11CreateDeviceAndSwapChain的原型如下

HRESULT D3D11CreateDeviceAndSwapChain(
  _In_opt_        IDXGIAdapter         *pAdapter,
                  D3D_DRIVER_TYPE      DriverType,
                  HMODULE              Software,
                  UINT                 Flags,
  _In_opt_  const D3D_FEATURE_LEVEL    *pFeatureLevels,
                  UINT                 FeatureLevels,
                  UINT                 SDKVersion,
  _In_opt_  const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
  _Out_opt_       IDXGISwapChain       **ppSwapChain,
  _Out_opt_       ID3D11Device         **ppDevice,
  _Out_opt_       D3D_FEATURE_LEVEL    *pFeatureLevel,
  _Out_opt_       ID3D11DeviceContext  **ppImmediateContext
);

  每个参数的含义如下:
* IDXGIAdapter *pAdapter:创建设备时使用的视频适配器指针,使用NULL表示使用默认的适配器.
* D3D_DRIVER_TYPE DriverType:描述驱动类型的枚举体,主要是说明使用什么方式去实现Direct3D特征.一般我们常用两种:D3D_DRIVER_TYPE_HARDWARE以及D3D_DRIVER_TYPE_SOFTWARE。其他的参数可以查看MSDN对该枚举体的描述
* HMODULE Software:该参数指向一个DLL,该DLL实现了软件光栅化处理。如果上一个参数设为D3D_DRIVER_TYPE_SOFTWARE,则不可以设为NULL,否则就取NULL。
* UINT Flags:描述用于创建设备的参数,它的取值定义在D3D11_CREATE_DEVICE_FLAG枚举体中,可以按位OR进行结合,一般设为0就可以了
* D3D_FEATURE_LEVEL *pFeatureLevels:目标功能级别集合,就是之前我们创建的数组
* UINT FeatyreLevels:这里输入上一个数组中元素的个数。
* UINT SKDVersion:Direct3D的版本号,我们使用D3D11_SDK_VERSION即可
* DXGI_SWAP_CHAIN_DESC *pSwapChainDesc:毫无疑问,这里填我们之前描述交换链时使用的结构体.
* IDXGISwapChain **ppSwapChain:待初始化的交换链接口.
* ID3D11Device **ppDevice:待初始化的设备接口.
* D3D_FEATURE_LEVEL *pFeatureLevel:这是一个输出参数,它指向刚才输入的数组中第一个被设备支持的目标功能级别.这个可以用来作判断哪一个功能级别可以匹配。一般我们不需要,把它设为NULL.
* ID3D11DeviceContext **ppImmediateContext:待初始化的立即执行上下文.

创建目标渲染视图

  前面提到了交换链,也介绍了交换链的概念,我们绘图的时候就是要将数据绘到后台去,通过交换链不断地交换到前台显示。
  因此要创建目标渲染视图,必须首先获得后台缓冲区。然后通过它创建目标渲染视图,接着,将创建好的目标渲染视图通过执行上下文设置一下。执行上下文描述了下一次绘图的状态,一般而言,目标渲染视图只有一个,因此设置一次,终生受用啊。
  因此,我们可以这样获取后台缓存

ID3D11Texture2D *pBackBuffer = NULL;//ID3D11Texture2D类型的,后台缓存指针
    //调用GetBuffer()函数得到后台缓存对象,并存入&pBackBuffer;
    hr = (*swapChain)->GetBuffer(0,//缓存索引,一般设为0
                        __uuidof(ID3D11Texture2D), //缓存类型
                        (LPVOID*)&pBackBuffer);//缓存指针

  然后通过设备指针创建目标渲染视图

(*device)->CreateRenderTargetView(pBackBuffer,//上面创建好的后台缓存
                                NULL,//设置为NULL得到默认的渲染目标视图
                                renderTargetView);//返回创建好的渲染目标视图
                                //InitD3D函数传递的实参

创建深度模板缓存视图

  要创建深度模板缓存视图,首先需要创建深度缓存,要创建深度缓存首先需要描述它,然后通过设备创建即可.
  要描述深度模板缓存,需要填充一个结构体D3D11_TEXTURE2D_DESC,该结构体用于描述2D纹理(View实际上就是一个二维的图像嘛),在MSDN上可以查到该结构体的原型如下:

typedef struct D3D11_TEXTURE2D_DESC1 {
  UINT                 Width;
  UINT                 Height;
  UINT                 MipLevels;
  UINT                 ArraySize;
  DXGI_FORMAT          Format;
  DXGI_SAMPLE_DESC     SampleDesc;
  D3D11_USAGE          Usage;
  UINT                 BindFlags;
  UINT                 CPUAccessFlags;
  UINT                 MiscFlags;
} D3D11_TEXTURE2D_DESC1;
  • UINT Width:纹理宽度,我们可以取窗口宽度来描述
  • UINT Height:纹理高度,我们可以取窗口高度来描述
  • UINT MipLevels:纹理映射级数,取1即可,表示使用多重采样纹理
  • UINT ArraySize:设置在纹理数组中纹理的个数,取1即可
  • DXGI_FORMAT Format:描述缓存格式的枚举类型,在深度模板缓存中,我们使用DXGI_FORMAT_D24_UNORM_S8_UINT.
  • DXGI_SAMPLE_DESC SampleDesc:描述多重采样参数的结构体。这里可以不设置
  • D3D11_USAGE Usage:标识了纹理是如何读取和写入的。可以取默认值D3D11_USAGE_DEFAULT
  • UINT BindFlags:标识纹理如何绑定到管线的。可以参考D3D11_BIND_FLAG枚举类型,这里我们使用D3D11_BIND_DEPTH_STENCIL
  • UINT CPUAccessFlags:标识如何允许CPU访问该缓存,如果不需CPU访问要可以设为0.
  • UINT MscFlags:描述不常见的资源选项。这里我们不需要,设为0即可

 &esmp;我们可以这样来描述一个深度缓存

D3D11_TEXTURE2D_DESC dsDesc;
    dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;  //这里表示24位用于深度缓存,8位用于模板缓存
    dsDesc.Width = 800;                             //深度模板缓存的宽度
    dsDesc.Height = 600;                            //深度模板缓存的高度
    dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;    //绑定标识符
    dsDesc.MipLevels = 1;
    dsDesc.ArraySize = 1;
    dsDesc.CPUAccessFlags = 0;                      //CPU访问标识符,0为默认值
    dsDesc.SampleDesc.Count = 1;                    //多重采样的属性,本例中不采用多重采样即,
    dsDesc.SampleDesc.Quality = 0;                  //所以Count=1,Quality=0
    dsDesc.MiscFlags = 0;
    dsDesc.Usage = D3D11_USAGE_DEFAULT;

  描述完深度缓存之后,就需要创建深度缓存,创建的过程是通过设备的CreateTexture2D来创建的,与目标渲染视图的创建过程一样,只是后者是通过交换链获取,而前者需要描述创建罢了.

device->CreateTexture2D(&dsDesc, 0, depthStencilBuffer);

  接着我们就可以直接拿着创建好的深度模板缓存,来创建深度模板缓存视图了

 device->CreateDepthStencilView(*depthStencilBuffer, 0, depthStencilView);

设置目标渲染视图和深度模板缓存视图

  这两个视图在刚才已经创建好了,我们直接使用设备上下文设置即可,只需要调用设备上下文的OMSetRenderTargets方法就可以完成设置
  该方法的原型如下

void OMSetRenderTargets(
  [in]           UINT                          NumViews,
  [in, optional] ID3D11RenderTargetView *const *ppRenderTargetViews,
  [in, optional] ID3D11DepthStencilView        *pDepthStencilView
);
  • UINT NumViews:字面意思,视图的数量
  • ID3D11RenderTargetView *const *ppRenderTargetViews:刚才创建好的目标渲染视图
  • ID3D11DepthStencilView *pDepthStencilView:刚才创建好的深度缓存模板视图
      下面是一个使用示例:
immediateContext->OMSetRenderTargets(1,                   //绑定的目标视图的个数
        renderTargetView,    //渲染目标视图,InitD3D函数传递的实参
        *depthStencilView);  //绑定模板

设置视口

  视口描述的是观察视角,在Direct3D中D3D11_VIEWPORT定义了视口的属性。关于视口的描述和DX9的一样,可以看看我在学习DX9时关于四大变换的笔记。

初步封装代码

#pragma once
#include<windows.h>
#include<d3dcompiler.h>
#include<xnamath.h>
#include<d3d11.h>
#include<d3dx11.h>

#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dx11.lib")
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib,"dxguid.lib")
#pragma comment(lib,"winmm.lib")

#define SAFE_RELEASE(p) {if((p)){(p)->Release();}}



class DirectSystem
{
//私有字段
private:
    //基础接口
    ID3D11Device *m_pDevice;                    //DX设备接口,用于生成各种各样的对象
    ID3D11DeviceContext *m_pDeviceContext;      //DX设备上下文,用于生成设备渲染指令

    //视图区域
    ID3D11RenderTargetView *m_pRenderTargetView;//DX渲染目标视图,字面意思
    ID3D11DepthStencilView *m_pDepthStencilView;//DX深度模板缓存视图
    ID3D11Texture2D        *m_pDepthStencil;    //深度模板缓存

    //图形接口
    IDXGISwapChain *m_pSwapChain;               //DX图形接口,交换链



public:
    DirectSystem()
        :m_pDevice(NULL),
         m_pDeviceContext(NULL),
         m_pRenderTargetView(NULL),
         m_pDepthStencilView(NULL),
         m_pDepthStencil(NULL),
         m_pSwapChain(NULL)
    {

    }

    HRESULT Direct3D_Init(HWND hwnd,int WINDOW_WIDTH,int WINDOW_HEIGHT) {
        //描述交换链
        DXGI_SWAP_CHAIN_DESC scDesc;
        ::ZeroMemory(&scDesc, sizeof(scDesc));
        scDesc.OutputWindow = hwnd;     //描述输出窗口
        scDesc.BufferCount = 1;//缓存数量
        scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//描述后台缓存格式
        scDesc.BufferDesc.Width = WINDOW_WIDTH; //描述后台缓存分辨率宽度
        scDesc.BufferDesc.Height = WINDOW_HEIGHT;//描述后台缓存分辨率高度
        scDesc.BufferDesc.RefreshRate.Denominator = 1;//刷新频率的分母
        scDesc.BufferDesc.RefreshRate.Numerator = 60;//刷新频率的分子,这两项表明每秒刷新6次
        scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
        scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        scDesc.Flags = 0;
        scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        scDesc.SampleDesc.Count = 1;    //这里不使用多重采样,因此数量设为1
        scDesc.SampleDesc.Quality = 0;  //不使用多重采样,设为0
        scDesc.Windowed = true;         //设置为窗口模式

        D3D_FEATURE_LEVEL featureLevels[] =
        {
            D3D_FEATURE_LEVEL_11_0,     //D3D11.0 所支持的功能特征
            D3D_FEATURE_LEVEL_10_1,     //D3D10.1 所支持的功能特征
            D3D_FEATURE_LEVEL_10_0      //D3D10.0 所支持的功能特征
        };
        UINT numFeature = ARRAYSIZE(featureLevels);

        HRESULT te;
        //创建设备,设备上下文,交换链
        if (FAILED(te=D3D11CreateDeviceAndSwapChain(
            NULL,
            D3D_DRIVER_TYPE_HARDWARE,NULL,0,
            featureLevels,numFeature,
            D3D11_SDK_VERSION,&scDesc,
            &m_pSwapChain,&m_pDevice,NULL,&m_pDeviceContext)))
        {
            MessageBox(NULL, L"Create Device and Swapchain Error", L"Error", NULL);
            return E_FAIL;
        }

        //获取后台缓冲区
        ID3D11Texture2D *pBack = NULL;
        if (FAILED(m_pSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),(void**)&pBack))) {
            MessageBox(NULL, L"GetBuffer Error", L"Error", NULL);
            return E_FAIL;
        }

        //创建渲染目标视图
        HRESULT hr;
        hr=m_pDevice->CreateRenderTargetView(pBack, NULL, &m_pRenderTargetView);

        SAFE_RELEASE(pBack);
        if (FAILED(hr))
        {
            MessageBox(NULL, L"Create Render Target View Error", L"Error", NULL);
            return E_FAIL;
        }

        //描述深度模板缓存
        D3D11_TEXTURE2D_DESC dsDesc;
        ::ZeroMemory(&dsDesc, sizeof(dsDesc));
        dsDesc.ArraySize = 1;
        dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
        dsDesc.CPUAccessFlags = 0;
        dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        dsDesc.Height = WINDOW_HEIGHT;
        dsDesc.Width  = WINDOW_WIDTH;
        dsDesc.MipLevels = 1;
        dsDesc.MiscFlags = 0;
        dsDesc.SampleDesc.Count = 1;
        dsDesc.SampleDesc.Quality = 0;
        dsDesc.Usage = D3D11_USAGE_DEFAULT;

        if (FAILED(m_pDevice->CreateTexture2D(&dsDesc, NULL, &m_pDepthStencil)))
        {
            MessageBox(NULL, L"Create Depth Stencil Buffer Error", L"Error", NULL);
            return E_FAIL;
        }

        if (FAILED(m_pDevice->CreateDepthStencilView(m_pDepthStencil, 0, &m_pDepthStencilView)))
        {
            MessageBox(NULL, L"Create Depth Stencil Buffer View Error", L"Error", NULL);
            return E_FAIL;
        }

        m_pDeviceContext->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);

        //第四步,设置视口大小,D3D11默认不会设置视口,此步骤必须手动设置  
        D3D11_VIEWPORT vp;    //创建一个视口的对象
        vp.TopLeftX = 0;      //视口左上角的横坐标
        vp.TopLeftY = 0;      //视口左上角的总坐标
        vp.Width = WINDOW_WIDTH;     //视口的宽
        vp.Height = WINDOW_HEIGHT;   //视口的高
        vp.MinDepth = 0.0f;   //深度值的下限,**由于深度值是[0, 1]所以下限值是0
        vp.MaxDepth = 1.0f;   //深度值的上限,上限值是1

        m_pDeviceContext->RSSetViewports(1, &vp);

        return S_OK;

    }

    void Direct3D_Render()
    {
        if (m_pDevice)
        {
            FLOAT color[4] = { 1.0f,0.0f,0.0f,0.0f };
            m_pDeviceContext->ClearRenderTargetView(m_pRenderTargetView,color);
            m_pSwapChain->Present(NULL, NULL);
        }

    }

    bool SetUp()
    {
        return true;
    }

    ~DirectSystem()
    {
        SAFE_RELEASE(m_pDevice);
        SAFE_RELEASE(m_pDeviceContext);
        SAFE_RELEASE(m_pRenderTargetView);
        SAFE_RELEASE(m_pDepthStencilView);
        SAFE_RELEASE(m_pDepthStencil);
        SAFE_RELEASE(m_pSwapChain);

    }
};

  最后的运行效果将会是一个红色的窗口。


Copyright© by 寒江雪
Date:2016.12.25

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页