• (转)【D3D11游戏编程】学习笔记五:D3D11初始化


    (注:【D3D11游戏编程】学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~)

           初次使用D3D11,先从它的初始化开始。不过在使用D3D之前,需要了解几个重要的概念:

           1. 硬件能力:Hardware Capacity

           熟悉D3D9的会很清楚,在初始化d3d9的一开始需要做的就是检测硬件的能力,以了解该用户机器支持哪些d3d特性,哪些不支持,以在运行期合理的调用API。除非使用d3d自带的“软件渲染引擎”,否则试图使用硬件不支持的特性是会出错的。但是在d3d11中,检测硬件能力这一步不再有必要。这对程序员来说省了一点功夫,但是额外的要求是,该硬件必须支持d3d11的全部特性!因此实际上,d3d11应用程序对硬件的要求更苛刻了。

           2. 数据格式

           D3D应用程序中,无论是纹理图片,还是创建的缓冲区,都有着特定的数据格式。D3D11支持有限的数据格式,以枚举变量形式存在,如下几种:

           DXGI_FORMAT_R32G32B32_FLOAT: 3个32位单精度符点数组成,比如用于代表三维空间坐标,以及24位颜色;

           DXGI_FORMAT_R16G16B16A16_UNORM: 4个16位数组成,每个成员位于[0,1.0f]之间,UNORM意指:unsigned normalized,即无符号,且归一化的;

           DXGI_FORMAT_R32G32_UINT:2个32位数组成,每个成员为无符号整型(unsigned int);

           DXGI_FORMAT_R8G8B8A8_UNORM:4个8位数组成,每个成员为[0,1.f]之间;

           DXGI_FORMAT_R8G8B8A8_SNORM:4个8位数组成,每个成员为[-1.0f, 1.0f]之间,SNORM意指:signed normalized;

           DXGI_FORMAT_R8G8B8A8_SINT:4个8位数组成,每个成员为有符号整型;

           此外还有很多其他类型的数据格式,熟悉各部分的意义后对任何类型可以很快从名字中得知其意思,也很容易写出指定意义的数据格式对应的枚举变量。数据格式在程序中使用相当频繁,因此很有必要提前熟悉该枚举类型变量的特点。

           3. DXGI

           DXGI,即DirectX Graphics Infrastructure,是从d3d10开始出现的,封装了一些d3d底层、基础的机制,包括数据格式定义、交换链(SwapChain),枚举设备类型及检测设备信息等。由于这些机制适合于任何一代的D3D接口,而跟特定的d3d特性无关,更新远不及其他d3d特性频繁,因此微软把这些从direct3d中单独分离了出来。这部分对应的枚举变量及函数等都以DXGI开头。

           4. 交换链:SwapChain

           交换链不是d3d11特有的性质,而是任何2D、3D计算机动画最基本的机制之一。为了实现平滑的动画,至少需要两个缓冲区,一个前缓冲区用于显示,一个后缓冲区用于下一帧的绘制,每次绘制完一帧后通过交换前、后缓冲区对应的指针来显示新一帧,并在之前的前缓冲区(当前的后缓冲区)上开始继续绘制下一帧。交换链可以有3个或者更多缓冲欧,但绝大多数情况下,2个已经足够了。在d3d11中交换链对应的接口为IDXGISwapChain。

           5. 深度/模板缓冲区:Depth/Stencil Buffer

           深度缓冲区是与交换链缓冲区大小完全一样的一块显存区域,即每个像素在深度缓冲区中对应相应的位置。在渲染管线的最终的混合阶段(Output Merger Stage),每个片段(Fragment)都有一个深度值z,与深度缓冲区对应位置上的深度相比较,如果该片段z更小,则绘制该片段,并覆盖当前的尝试值,否则抛弃该片段。该缓冲区主要用于实现投影在屏幕上同一位置、远近不同的物体之间相同的遮挡效果。此外,灵活配置尝试缓冲区,可以实现很多种高级特效。

           模板缓冲区与深度缓冲区共享同一块区域,每个深度值处对应一个模板值,模板值主要用于实现一些高级的特效,比如平面反射、阴影等。利用深度、模板缓冲区的不同的configuration实现各种特效在后面会有详细介绍的。

           6. 视图(View)

           视图是D3D11中的最重要的概念之一。以纹理为例,一个纹理可以被绑定到3D渲染管线的不同阶段以实现不同的用途。比如常见的纹理图片(d3d11中叫Shader Resource),另一方面也可用于渲染对象(Render Target),充当交换链中的缓冲区,用于绘制场景。同一个纹理可以同时被绑定到多个管线阶段(Dynamic Cube Mapping就是一个例子,后面会专门介绍),但在创建该纹理资源时需要指定其绑定的类型(Bind Flags),比如上述例子,绑定类型要指定为:D3D11_BIND_RENDER_TARGET | D3D11_SHADER_RESOURCE。

           实际上,在d3d11中,绑定到管线某阶段的并不是纹理资源本身,而是对应的“视图”。对于使用该纹理的每一种方式,我们分别创建相应的视图,然后把视图绑定到管线特定的阶段。D3D11中针对每种视图相应的接口为:ID3D11ShaderResourceView和ID3D11RenderTargetView、ID3D11DepthStencilView。使用视图的具体细节在后面的过程中会慢慢体会到。

           7. 多重采样抗锯齿:Multisampling Atialiasing

           针对光栅化显示器抗锯齿的方法有多种,在d3d中采用的多重采样方法。即在每个像素点内部,设置多个采样点,绘制多边形边缘时,针对每个采样点判断是否被多边形覆盖,最终的颜色值从采样点中取均值,以对多边形的边缘进行“模糊化",从而减轻锯齿效果。如下图所示,这是一个4重采样的例子,该像素最终的颜色值是多边形本身颜色值的3/4:

    支持d3d11的硬件全部支持4重采样,因此我们在后面的程序中将普遍使用4个采样点。在d3d11中通过结构DXGI_SAMPLE_DESC来设置多重采样,其定义如下:

    [cpp] view plain copy
    1. typedef struct DXGI_SAMPLE_DESC {  
    2.   UINT Count;  
    3.   UINT Quality;  
    4. } DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;  

    Count为我们设置的采样的个数,Quality为机器支持的不同的等级,初始化过程中我们会对Quality进行检测。

    注意Multisampling区别于super sampling的关键点:不同采样点是分别计算颜色值(super sampling)还是共享同一颜色值(Multisampling)。详细情况有很多参考书介绍。

           8. 特征等级:Feature Level

           特征等级定义了一系列支持不同d3d功能的相应的等级,用意即如果一个用户的硬件不支持某一特征等级,程序可以选择较低的等级,以确保正确地运行。d3d11中定义了如下几个等级以代表不同的d3d版本:

    [cpp] view plain copy
    1. typedef enum D3D_FEATURE_LEVEL {  
    2.   D3D_FEATURE_LEVEL_9_1    = 0x9100,  
    3.   D3D_FEATURE_LEVEL_9_2    = 0x9200,  
    4.   D3D_FEATURE_LEVEL_9_3    = 0x9300,  
    5.   D3D_FEATURE_LEVEL_10_0   = 0xa000,  
    6.   D3D_FEATURE_LEVEL_10_1   = 0xa100,  
    7.   D3D_FEATURE_LEVEL_11_0   = 0xb000   
    8. } D3D_FEATURE_LEVEL;  

    在初始化过程中,我们可以提供一组不同的特征等级,程序会从第一个开始逐个检测,碰到第一个合适的来创建设备。因此我们在数组中从高到低放置特征等级提供给初始化程序。

          

           OK,重要的概念就是这些,现在开始初始化D3D~

           D3D11的初始化主要有以下几个步骤:

           1. 创建设备ID3D11Device和设备上下文ID3D11DeviceContext;

           2. 检测多重采样支持的等级:CheckMultisampleQualityLevels

           3. 创建交换链

           4. 创建RenderTargetView

           5. 创建DepthStencilView

           6. 把上述两个视图绑定到渲染管线相应的阶段

           7. 设置Viewport

           下面从第一步开始:

           1. 创建设备及上下文:

           函数原型如下:

    [cpp] view plain copy
    1. HRESULT  D3D11CreateDevice(  
    2.  __in   IDXGIAdapter *pAdapter,  
    3.  __in   D3D_DRIVER_TYPE DriverType,  
    4.  __in   HMODULE Software,  
    5.  __in   UINT Flags,  
    6.  __in   const D3D_FEATURE_LEVEL *pFeatureLevels,  
    7.  __in   UINT FeatureLevels,  
    8.  __in   UINT SDKVersion,  
    9.  __out  ID3D11Device **ppDevice,  
    10.  __out  D3D_FEATURE_LEVEL *pFeatureLevel,  
    11.  __out  ID3D11DeviceContext **ppImmediateContext  
    12. ;  

           pAdapter来选择相应的图形适配器,设为NULL以选择默认的适配器;

           DriverType设置驱动类型,一般毫无疑问选择硬件加速,即D3D_DRIVER_TYPE_HARDWARE,此时下一个参数就是NULL;

           Flags为可选参数,一般为NULL,可以设为D3D11_CREATE_DEVICE_DEBUG、D3D11_CREATE_DEVICE_SINGLETHREADED,或两者一起,前者让要用于调试时收集信息,后者在确定程序只在单线程下运行时设置为它,可以提高性能;

           pFeatureLevels为我们提供给程序的特征等级的一个数组,下一个参数为数组中元素个数;

           SDKVersion恒定为D3D11_SDK_VERSION;

           ppDevice为设备指针的地址,注意设备是指针类型,这里传递的是指针的地址(二维指针,d3d程序中所有的接口都声明为指针类型!);

           pFeatureLevel为最后程序选中的特征等级,我们定义相应的变量,传递它的地址进来;

           ppImmediateContext为设备上下文指针的地址,要求同设备指针。

           创建设备代码如下:

    [cpp] view plain copy
    1. ID3D11Device        *d3dDevice(NULL);  
    2. ID3D11DeviceContext *deviceContext(NULL);  
    3. D3D_FEATURE_LEVEL featureLevels[6] = {  
    4.     D3D_FEATURE_LEVEL_11_0,  
    5.     D3D_FEATURE_LEVEL_10_1,  
    6.     D3D_FEATURE_LEVEL_10_0,  
    7.     D3D_FEATURE_LEVEL_9_3,  
    8.     D3D_FEATURE_LEVEL_9_2,  
    9.     D3D_FEATURE_LEVEL_9_1};  
    10. D3D_FEATURE_LEVEL   curLevel;  
    11. D3D11CreateDevice(  
    12.     0,                  //默认适配器  
    13.     D3D_DEVICE_TYPE_HARDWARE,   //硬件加速设备类型  
    14.     0,   
    15.     0,   
    16.     featureLevels, 6,  
    17.     D3D11_SDK_VERSION,  
    18.     &d3dDevice,  
    19.     &curLevel,  
    20.     &deviceContext);  

          创建完即可检测多重采样(我们只用4重采样)的等级:

    [cpp] view plain copy
    1. m_d3dDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM,4,&x4MsaaQuality);  

            2. 创建交换链

           交换链性质通过如下结构指定:

    [cpp] view plain copy
    1. typedef struct DXGI_SWAP_CHAIN_DESC {  
    2.   DXGI_MODE_DESC   BufferDesc;  
    3.   DXGI_SAMPLE_DESC SampleDesc;  
    4.   DXGI_USAGE       BufferUsage;  
    5.   UINT             BufferCount;  
    6.   HWND             OutputWindow;  
    7.   BOOL             Windowed;  
    8.   DXGI_SWAP_EFFECT SwapEffect;  
    9.   UINT             Flags;  
    10. } DXGI_SWAP_CHAIN_DESC;  

           BufferDesc指定后缓冲区有关特性;

           SampleDesc指定多重采样,前面说过;

           BufferUsage,对于交换链,为DXGI_USAGE_RENDER_TARGET_OUTPUT;

           BufferCount:我们只创建一个后缓冲区(双缓冲),因此为1;

           OutputWindow:指定窗口句柄,Win32程序初始化完创建的主窗口;

           Windowed:是否全屏;

           DXGI_SWAP_EFFECT:通常为DXGI_SWAP_EFFECT_DISCARD;

           Flags:可选,我们设为0;

    DXGI_MODE_DESC结构定义如下:

    [cpp] view plain copy
    1. typedef struct DXGI_MODE_DESC {  
    2.   UINT                     Width;  
    3.   UINT                     Height;  
    4.   DXGI_RATIONAL            RefreshRate;  
    5.   DXGI_FORMAT              Format;  
    6.   DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;  
    7.   DXGI_MODE_SCALING        Scaling;  
    8. } DXGI_MODE_DESC, *LPDXGI_MODE_DESC;  

           Width、Height为缓冲区大小,一般设为主窗口大小;

           Format为缓冲区类型,一般作为渲染对象缓冲区类型为DXGI_FORMAT_R8G8B8A8_UNORM;

           其他参数一般为固定的,参见代码。

           此外,创建交换链需要获得接口IDXGIFactory,过程是固定的,创建交换链代码如下:

    [cpp] view plain copy
    1. DXGI_SWAP_CHAIN_DESC scDesc = {0};      //填充结构,设置交换链相当属性  
    2. scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;      //缓冲区数据格式  
    3. scDesc.BufferDesc.Width = 640;          //缓冲区大小  
    4. scDesc.BufferDesc.Height = 480;  
    5. scDesc.BufferDesc.RefreshRate.Numerator = 60;           //刷新率,一般这样设定即可  
    6. scDesc.BufferDesc.RefreshRate.Denominator = 1;  
    7. scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;          //固定参数  
    8. scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;  //固定参数  
    9. scDesc.BufferCount = 1;                 //缓冲区个数  
    10. scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;       //Usage为Render Target Output  
    11. scDesc.Flags = 0;  
    12. scDesc.OutputWindow = m_hWnd;           //主窗口句柄  
    13. scDesc.SampleDesc.Count = 4;            //4个采样点  
    14. scDesc.SampleDesc.Quality = x4MsaaQuality-1;    //4重采样支持等级  
    15. scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;   //常用参数  
    16. scDesc.Windowed = true;             //窗口模式  
    17.   
    18. //通过如下三步获得接口IDXGIFactory,来创建交换链  
    19. IDXGIDevice *pDxgiDevice(NULL);  
    20. m_d3dDevice->QueryInterface(__uuidof(IDXGIDevice),reinterpret_cast<void**>(&pDxgiDevice));  
    21. IDXGIAdapter *pDxgiAdapter(NULL);  
    22. pDxgiDevice->GetParent(__uuidof(IDXGIAdapter),reinterpret_cast<void**>(&pDxgiAdapter));  
    23. IDXGIFactory *pDxgiFactory(NULL);  
    24. pDxgiAdapter->GetParent(__uuidof(IDXGIFactory),reinterpret_cast<void**>(&pDxgiFactory));  
    25. pDxgiFactory->CreateSwapChain(d3dDevice,&scDesc,&swapChain);  
    26.   
    27. //释放接口  
    28. SafeRelease(pDxgiFactory);  
    29. SafeRelease(pDxgiAdapter);  
    30. SafeRelease(pDxgiDevice);  

           3. 创建RenderTargetView

           D3D11中创建视图需要对应的资源,这里先获取后缓冲区地址,作为参数创建相应的视图:

    [cpp] view plain copy
    1. ID3D11Texture2D *backBuffer(NULL);  
    2. //获取后缓冲区地址  
    3. swapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&backBuffer));  
    4. //创建视图  
    5. d3dDevice->CreateRenderTargetView(backBuffer,0,&m_renderTargetView);  
    6. //释放后缓冲区引用  
    7. backBuffer->Release();  

           CreateRenderTargetView函数原型如下:

    [cpp] view plain copy
    1.     HRESULT CreateRenderTargetView(  
    2.      ID3D11Resource *pResource,             //视图对应资源  
    3.      const D3D11_RENDER_TARGET_VIEW_DESC *pDesc,        //视图描述  
    4.      ID3D11RenderTargetView **ppRTView          //要创建的视图(指针的地址)  
    5. );  

           d3d11中创建视图时需要用到视图的描述(第二个参数),但如果视图对应的资源类型已知,则一般不再需要描述,设为NULL。这里后缓冲区在创建时类型已给出(DXGI_FORMAT_R8G8B8A8_UNORM),因此这里不需要视图描述。资源创建时也可以指定为无类型(TYPELESS),在不同阶段以不同的类型进行解释,这时创建视图时就需要视图描述明确说明类型。
           4. 创建深度、模板缓冲区及对应视图

           创建缓冲区要即创建一个2维纹理,ID3D11Texture2D,创建它需要先给出描述D3D11_TEXTURE2D_DESC。定义如下:

    [cpp] view plain copy
    1. typedef struct D3D11_TEXTURE2D_DESC {  
    2.   UINT             Width;  
    3.   UINT             Height;  
    4.   UINT             MipLevels;       //这里不需要mipmap,设为1  
    5.   UINT             ArraySize;       //纹理数组才用,这里为1  
    6.   DXGI_FORMAT      Format;      //数据格式,一般为DXGI_FORMAT_D24_UNORM_S8_UINT,24位用于深度,8位用于模板  
    7.   DXGI_SAMPLE_DESC SampleDesc;      //多重采样,如前,前后务必保持一致!  
    8.   D3D11_USAGE      Usage;       //Usage,对于只让GPU读、写,应为D3D11_USAGE_DEFAULT  
    9.   UINT             BindFlags;       //绑定类型,为D3D11_BIND_DEPTH_STENCIL  
    10.   UINT             CPUAccessFlags;  //CPU不可访问,设为0  
    11.   UINT             MiscFlags;       //设为0  
    12. } D3D11_TEXTURE2D_DESC;  

           创建视图类型DepthStencilView,代码如下:

    [cpp] view plain copy
    1. D3D11_TEXTURE2D_DESC dsDesc;  
    2. dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;  
    3. dsDesc.Width = 640;  
    4. dsDesc.Height = 480;  
    5. dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;  
    6. dsDesc.MipLevels = 1;  
    7. dsDesc.ArraySize = 1;  
    8. dsDesc.CPUAccessFlags = 0;  
    9. dsDesc.SampleDesc.Count = 4;  
    10. dsDesc.SampleDesc.Quality = x4MsaaQuality-1;  
    11. dsDesc.MiscFlags = 0;  
    12. dsDesc.Usage = D3D11_USAGE_DEFAULT;  
    13. d3dDevice->CreateTexture2D(&dsDesc,0,&depthStencilBuffer);  
    14. d3dDevice->CreateDepthStencilView(depthStencilBuffer,0,&depthStencilView);  

    创建完两个视图后当然就要绑定到渲染管线上去啦:

    [cpp] view plain copy
    1. deviceContext->OMSetRenderTargets(1,&renderTargetView,depthStencilView);  

    该函数原型如下:

    [html] view plain copy
    1. void OMSetRenderTargets(  
    2.   [in]  UINT NumViews,                          //RenderTarget个数,我们一般只用一个  
    3.   [in]  ID3D10RenderTargetView *const *ppRenderTargetViews,     //RenderTarget数组,只一个,所以直接传其地址即可  
    4.   [in]  ID3D10DepthStencilView *pDepthStencilView           //DepthStencil view  
    5. );  

           最后设置viewport,很简单,D3D11_VIEWPORT定义如下:

    [cpp] view plain copy
    1. typedef struct D3D11_VIEWPORT {  
    2.   FLOAT TopLeftX;       //视口左上角在屏幕上x坐标,一般视口占满屏幕的,所以为0  
    3.   FLOAT TopLeftY;       //y坐标,同上  
    4.   FLOAT Width;          //视口宽度,一般与后缓冲区一致,以保持图像不变形  
    5.   FLOAT Height;         //高度,同上  
    6.   FLOAT MinDepth;       //最小深度值:0.0f  
    7.   FLOAT MaxDepth;       //最大深度值:1.0f  
    8. } D3D11_VIEWPORT;  

           设置视口代码:

    [cpp] view plain copy
    1. D3D11_VIEWPORT viewPort;  
    2. viewPort.Width = static_cast<FLOAT>(clientWidth);  
    3. viewPort.Height = static_cast<FLOAT>(clientHeight);  
    4. viewPort.MaxDepth = 1.f;  
    5. viewPort.MinDepth = 0.f;  
    6. viewPort.TopLeftX = 0.f;  
    7. viewPort.TopLeftY = 0.f;  
    8. deviceContext->RSSetViewports(1,&viewPort);  

           Voila,初始化过程全部完成!

           下面就可以进入主循环渲染函数绘制场景了,这次我们暂时什么也不画,只是简单地把屏幕清空为随意指定的颜色:

    [cpp] view plain copy
    1. XMVECTORF32 color = {0.f, 1.f, 0.f, 1.6f};  
    2. g_deviceContext->ClearRenderTargetView(g_renderTargetView,reinterpret_cast<float*>(&color));  
    3. g_deviceContext->ClearDepthStencilView(g_depthStencilView,D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL,1.f,0);  
    4.   
    5. g_swapChain->Present(0,0);  

           每帧开始渲染时,先清空RenderTargetView和深度缓冲区,然后就是全屏渲染内容了。绘制完整个场景后,最后调用让交换链Present来交换前后缓冲区,以显示新的一帧。清空RenderTarget很简单,把相应视图传进来,并给定任意背景颜色(4个float的数组,这里适合用XMVECTORF32)即可。对于清空深度、模板缓冲区,第一个参数一目了然,第二个用来指定清空的内容参数,一般我们要同时清空深度和模板,因此用两个枚举变量求并,最后两个参数分别指定清空深度和模板的默认值,深度清空为1.0f(最大),模板一般清空为0,就这样。
          

           整个过程就结束啦,配合上次中的Win32程序,现在已经是一个完整的、可工作的d3d程序了,显示的即为一个空白的指定颜色的窗口。初始化程序就是这样,一个最简单的d3d应用程序。以后慢慢开始渲染更加炫丽的场景~

           这次完整的初始化程序源代码如下:

           D3D11完整初始化代码

  • 相关阅读:
    git查看工作状态和历史提交
    PowerDesigner工具栏palette的方法
    WCF证书制作
    ASP.NET.4 高级程序第4版 第3章Web窗体
    tbar居右显示的两种方法
    测试
    转载extj grid
    正值
    网站HTML,XHTML,XML,WML,CSS等测试验证工具介绍[转]
    SQL Server 启用“IP+端口”连接
  • 原文地址:https://www.cnblogs.com/wodehao0808/p/6603841.html
Copyright © 2020-2023  润新知