DX11 游戏开发笔记 (二) DX11 基础框架 上
阅读原文时间:2021年04月27日阅读:2

必须吐槽csdn的排版,真的是对不起它的名字!

抱了很大的决心,才决定写下这篇博文,笔者大三从其它专业转行,学习c c++也就一年多,因为入了游戏这坑,

故选择在游戏引擎这块泥泞的道路上前进,且行且开心吧。

不得不吐槽一句,国内想学游戏开发还真是有点难度,幸好有前辈的发掘,我们现在才好走一点。

前辈博客:https://blog.csdn.net/bonchoix?t=1 。

近来看了《大话设计模式》,觉得编者的讲解方式真是新奇有趣又贴入主题,故此笔者模仿其写法,试试能不能将DX11回顾的更有意思一些。

人物介绍:小白(熟悉win32,c++,c,数据结构与算法);

人物介绍: 老鸟(一名图形与游戏引擎大牛)。

小白:啦啦啦,(哼着歌开心的走进门)。

老鸟:发生什么事了?这么开心,是不是想请我吃饭啦。

小白:我今天开“阿卡丽神秘商店”开到了一折,而且里面还有我最想要的“刀妹”的皮肤,

          只需9.9元就可以买到它啦。

老鸟:切,我还以为你涨工资,要请我吃饭了,原来是因为要花钱买皮肤,真是:土豪,这么有钱,

         请我搓一顿怎么样? 不过3D游戏还真是有吸引力,能让你这么开心。

小白:是呀,但我不只玩游戏的,我还看它优异的画质,流畅的动画,精细的模型,打击的真实感,

           引人入胜的剧情关卡设计……….

老鸟:看样子你对游戏还有一番见解呀,那你知道3d游戏底层是怎么实现的吗?你想不想一探

          游戏引擎这个新大陆呢?了解之后就可以跟室友吹嘘啦。          

小白:瞧你说的,我是显摆的人吗,你跟我讲讲呗,我也想开拓一下见识。

老鸟:3D游戏通常是由引擎写出来的,一个好的大型的游戏往往有其自己的引擎,现在比较优秀的

          是“虚幻引擎”,但所有引擎都  有最基本的构建,就拿windows这个平台来说,我们最喜欢和

          最该接触的就是DX,通常,我们讲到DirectX的时候,都是指的Direct3D,因为Direct3D

          作为正面对抗OpenGL的重要武器,受到了微软最多的关怀与多次针对性的更新。所以Direct3D

         在DirectX的 阵营中的认知度最高,它的光芒完全掩盖了其他DirectX API的发挥空间。DirectX

         中其他的大多数 API有些被更新换代淘汰掉了,或者有轻微的变化,今天我们就来讲DX11。

小白:哇,感觉好庞大的样子。

老鸟:当然啦,不然怎么能做出这么多优秀的游戏了,

          我记得你刚刚学习完win32,写一个简单窗口应该没问题吧。

小白:那当然了,我写给你看看。

五分钟后,小白写的代码如下:

#include <windows.h>

#define  SCREEN_WIDTH  800
#define  SCREEN_HEIGHT 600
#define  WINDOW_TITLE    L"【D3D11初始化demo】"

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

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
    WCHAR className[32] = L"D3D11_demo_01";

    WNDCLASS Wnd;
    Wnd.cbClsExtra = NULL;
    Wnd.cbWndExtra = NULL;
    Wnd.hCursor = NULL;
    Wnd.style = CS_VREDRAW | CS_HREDRAW;
    Wnd.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    Wnd.hIcon = NULL;
    Wnd.hInstance = hInstance;
    Wnd.lpfnWndProc = WndProc;
    Wnd.lpszClassName = className;
    Wnd.lpszMenuName = NULL;

    if (!RegisterClass(&Wnd))
    {
        return -1;
    }

    int Posx = 200;
    int Posy = 100;

    HWND hWnd = CreateWindow(className, WINDOW_TITLE, 
        WS_SYSMENU | WS_OVERLAPPED | WS_MINIMIZEBOX,
        Posx, Posy, SCREEN_WIDTH, SCREEN_HEIGHT,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, nShowCmd);
    UpdateWindow(hWnd);


    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
            DestroyWindow(hWnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

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

老鸟:嗯恩,不错,看来你win32还是掌握好基础了,学习DX也就需要这些,我先为你介绍DX11这座大营:

          ID3D11Device*     md3dDevice:

D3D11设备指针,掌管DX11的生死大权,为硬汉集团首领。

ID3D11DeviceContext*   md3dImmediateContext:

D3D11设备上下文,即为DX11的监军(副帅),为智囊团的代表。

IDXGISwapChain* md3dSwapChain:

D3D交换链,为军队的兵器马匹,为战争需准备的资源。

           DX11军营统兵调度需此三样,缺一不可。

           但是我们第一步要做的只是指明这三人或物,即声明,但不着急着用。

#define HR(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}
ID3D11Device* md3dDevice;//D3D11设备
ID3D11DeviceContext* md3dImmediateContext;//D3D11设备上下文
IDXGISwapChain* md3dSwapChain;//D3D交换链

俗话说,兵马未动,粮草先行,我们得先去要圣旨:   IDXGIFactory* factory;

//创建一个Directx图形接口factory
    HR(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory));

获得圣旨的许可权了,我们需派一个总领:IDXGIAdapter* adpter;//适配器,

//使用factory来为显卡创建一个adapter
    HR(factory->EnumAdapters(0, &adpter));

总领然后就开始点去粮仓的小弟:IDXGIOutput* adapterOutput;

//列举主要的适配器输出
    HR(adpter->EnumOutputs(0, &adapterOutput));

小弟就去粮仓了,然后记录并回报适合本次战役的粮仓信息。

首先记录符合初步规定粮仓的信息和数目: unsigned int numModes;

而后建立一个对应数量的记录本:DXGI_MODE_DESC* displayModeList;

再把相应数目的粮仓信息记录。

        unsigned int numModes;
        DXGI_MODE_DESC* displayModeList;

//获取适应适配器DXGI_FORMAT_R8G8B8A8_UNORM显示格式的模式数目
    HR(adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 
             DXGI_ENUM_MODES_INTERLACED, &numModes, NULL));

    //创建一个显示模式列表存放可能的显示模式(显卡,监视器)
    displayModeList = new DXGI_MODE_DESC[numModes];
    if (!displayModeList)
        return false;

    //填充显示模式列表结构体
    HR(adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 
             DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList));

接下来计算最符合本次战役粮仓运粮的效率:

fps = numerator / denominator;

分子numerator:时间t内刷新次数    (运粮量)

分母denominator:t(时间)

unsigned int numerator, denominator;
for (int i = 0; i<(int)numModes; i++)
    {
        if (displayModeList[i].Width == (unsigned int)SCREEN_WIDTH)
        {
            if (displayModeList[i].Height == (unsigned int)SCREEN_HEIGHT)
            {
                numerator = displayModeList[i].RefreshRate.Numerator;
                denominator = displayModeList[i].RefreshRate.Denominator;
            }
        }
    }

好了,这样事情也就做完了,该复命的复命,该释放的释放。

    //释放显示模式列表
    delete[] displayModeList;
    displayModeList = NULL;
    ReleaseCOM(adpter);
    ReleaseCOM(factory);

休息一下:

还记的笔者第一次玩lol选的就是寒冰,如今寒冰也从当年丛林小野猫变为了源战士,笔者更是度过了青春。

continue:

小白:挺有意思的,大哥,继续呀。

老鸟:好哒,粮草事宜准备好后,我们应该去准备兵马:

DXGI_SWAP_CHAIN_DESC sd;//交换链形容结构体

接下来就是配置属性:

        DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferDesc.Width = SCREEN_WIDTH;
    sd.BufferDesc.Height = SCREEN_HEIGHT;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

        bool mVsyncEnable = true;  //是否限帧渲染
    if (mVsyncEnable) //限不限帧
    {
                sd.BufferDesc.RefreshRate.Numerator =60;
        sd.BufferDesc.RefreshRate.Denominator = 1;

    }
    else
    {
                sd.BufferDesc.RefreshRate.Numerator = numerator;
        sd.BufferDesc.RefreshRate.Denominator = denominator;
    }

    //关闭多重采样
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;

    //是否进行全屏
    bool fullscreen = false;
    if (fullscreen)
    {
        sd.Windowed = false;
    }
    else
    {
        sd.Windowed = true;
    }
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.BufferCount = 1;  //背后缓存数量
    sd.OutputWindow = g_hWnd; //交换链所属的窗口
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    sd.Flags = 0;

老鸟:小白,听说你英文不错,翻译翻译一下上面sd的属性呗。

小白:嘻嘻,那还不是手到擒来。

经老鸟修改后出炉:

sd.BufferCount = 1;  交换链下后台缓存区数量。

sd.BufferDesc.Format:  交换链下缓冲区结构体下的像素格式。

sd.BufferDesc.ScanlineOrdering:交换链下缓冲区结构体下的排序扫描(光栅化)格式。

sd.BufferDesc.Scaling:交换链下缓冲区结构体下的缩放格式。

sd.SampleDesc.Count:交换链下采样结构体下的每个像素的采样数量。

sd.SampleDesc.Quality:交换链下采样结构体下的像素采样质量级别。

sd.OutputWindow = g_hWnd; 交换链所属的窗口句柄。

sd.SwapEffect:交换链下显卡驱动程序选择的显示模式。

sd.Flags:交换链下的全屏显示模式的可选设置。

sd.BufferUsage:交换链下的后台缓冲去的去处。

老鸟:这样你基本上就能看懂了,

小白:完全不呀,我看不懂他们设置的意义。

老鸟:好吧,好吧,那我就把DX11的相关介绍给你贴上:

创建交换链,首先需要填充一个 DXGI_SWAP_CHAIN_DESC 结构体来描述,该结构体的定义如下:

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 类型是另一个结构体,其定义如下:

typedef struct DXGI_MODE_DESC
{
UINT Width; // 后台缓冲区宽度
UINT Height; // 后台缓冲区高度
DXGI_RATIONAL RefreshRate; // 显示刷新率
DXGI_FORMAT Format; // 后台缓冲区像素格式
DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;// display scanline mode
DXGI_MODE_SCALING Scaling; // display scaling mode
} DXGI_MODE_DESC;

注意:在下面的数据成员描述中,我们只涵盖了一些常用的标志值和选项,它们对于初学者来说

非常重要。对于其他标志值和选项的描述,请参阅 SDK 文档。

1.BufferDesc:    该结构体描述了我们所要创建的后台缓冲区的属性。我们主要关注的属性有:

宽度、高度和像素格式;其他属性的详情请参阅 SDK 文档。

2.SampleDesc:  多重采样数量和质量级别。

typedef struct DXGI_SAMPLE_DESC {
UINT Count;
UINT Quality;
} DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;

3.BufferUsage:   设为 DXGI_USAGE_RENDER_TARGET_OUTPUT,因为我们要将
                                场景渲染到后台缓冲区(即将它用作渲染目标)。

4.BufferCount:    交换链中的后台缓冲区数量;我们一般只用一个后台缓冲区来实现双
                                缓存。当然,你也可以使用两个后台缓冲区来实现三缓存。

5.OutputWindow: 我们将要渲染到的窗口的句柄。

6.Windowed:       当设为 true 时,程序以窗口模式运行;当设为 false 时,程序以全屏
                                 (full-screen)模式运行。

7.SwapEffect:      设为 DXGI_SWAP_EFFECT_DISCARD,让显卡驱动程序选择最高效
                                  的显示模式。

8.Flags : 可 选 的 标 志 值 ,如 果 设 为DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,

那么当应用程序切换到全屏模式时,Direct3D 会自动选择与当前的后台缓冲区设置最匹配的显示模式。

如果未指定该标志值,那么当应用程序切换到全屏模式时,Direct3D 会使用当前的桌面显示模式。

我们在示例框架中没有使用该标志值,因为对于我们的演示程序来说,在全屏模式下使用当前的

桌面显示模式可以得到很好的效果。
 

老鸟:我不想贴出来的,以后这样的文档还是你自己去找才有意义,龙书11的资源写这篇博文的小主

          上篇博文里就有,你去找吧。

小白:辛苦你啦。

老鸟:嗯恩,我接受了,承接上文,DX11的兵马粮草准备好了,我们的两员大将也该带着兵马动身了,

    D3D_FEATURE_LEVEL featureLevel;
    featureLevel = D3D_FEATURE_LEVEL_11_0;
    HR(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,   
       &featureLevel, 1,D3D11_SDK_VERSION, &sd, &md3dSwapChain, &md3dDevice, NULL, 
&md3dImmediateContext));

老鸟:下面是通过vs2013看函数源文件找到的,这是很常用的学习方法,跟查msdn一样。

HRESULT WINAPI D3D11CreateDeviceAndSwapChain(

 __in_opt IDXGIAdapter* pAdapter,//指定要为哪个物理显卡创建设备对象。当该参数设为空值时, 
                                 //表示使用主显卡。在本书的示例程序中,我们只使用主显卡。
    D3D_DRIVER_TYPE DriverType,  //一般来讲,该参数总是指定为 D3D_DRIVER_TYPE_HARDWARE>
    HMODULE Software,//----------//用于支持软件光栅化设备(software rasterizer)。
                                 //我们总是将该参数设为0。
    UINT Flags,//----------------//可选的设备创建标志值。当以 release 模式生成程序时,
                                 //该参数通常设为 0。
                                 //当以 debug 模式生成程序时,该参数应设为:
                                 //D3D11_CREATE_DEVICE_DEBUG:用以激活调试层。
    __in_ecount_opt( FeatureLevels ) CONST D3D_FEATURE_LEVEL* pFeatureLevels,
    UINT FeatureLevels,
    UINT SDKVersion,             //始终设为 D3D11_SDK_VERSION。
    __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 );

老鸟:这函数中FeatureLevels你可能看不懂,这里我给你详解一下:

Direct3D 11 提 出 了 特 征 等 级 ( feature levels , 在 代 码 中 由 枚 举 类 型D3D_FEATURE_LEVEL 表示)的概念,

对应了定义了 d3d11 中定义了如下几个等级以代表不同的 d3d 版本:

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;

特征等级定义了一系列支持不同 d3d 功能的相应的等级(每个特征等级支持的功能可参见 SDK

文档),用意即如果一个用户的硬件不支持某一特征等级,程序可以选择较低的等级。例如,

为了支持更多的用户,应用程序可能需要支持 Direct3D 11,10.1,9.3 硬件。程序会从

最新的硬件一直检查到最旧的,即首先检查是否支持 Direct3D 11,第二检查 Direct3D 10.1,

然后是Direct3D 10,最后是 Direct3D 9。要设置测试的顺序,可以使用下面的特征等级数组。
(数组内元素的顺序即特征等级测试的顺序):

D3D_FEATURE_LEVEL featureLevels [4] =
{
D3D_FEATURE_LEVEL_11_0, // First check D3D 11 support
D3D_FEATURE_LEVEL_10_1, // Second check D3D 10.1 support
D3D_FEATURE_LEVEL_10_0, // Next,check D3D 10 support
D3D_FEATURE_LEVEL_9_3 // Finally,check D3D 9.3 support
} ;

这个数组可以放置在 Direct3D 初始化方法中,方法会输出数组中第一个可被支持的特征等级。

例如,如果 Direct3D 报告数组中第一个可被支持的特征等级是D3D_FEATURE_LEVEL_10_0,

程序就会禁用 Direct3D 11 和 Direct3D 10.1 的特征,而使用 Direct3D 10 的绘制路径。

老鸟:下面我们就来实际用下:

D3D_FEATURE_LEVEL features[3] ={D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3};
D3D_FEATURE_LEVEL myFeatureLevel;

HRESULT hr = D3D11CreateDevice(NULL,D3D_DRIVER_TYPE_HARDWARE,0,0,
   features,3,D3D11_SDK_VERSION,&g_device,&myFeatureLevel,&g_deviceContext);

if(FAILED(hr))
    {
        MessageBox(NULL,L"创建d3d11设备失败!",L"错误",MB_OK);
        return FALSE;
    }

  本例采用的是D3D11CreateDevice,跟D3D11CreateDeviceAndSwapChain相比其实就少了交换链的部分,很容易就能理解。

ps:

       (函数第5个参数)

          __in_ecount_opt( FeatureLevels ) CONST D3D_FEATURE_LEVEL* pFeatureLevels

         就是特征数组。

        (函数第6个参数)

UINT FeatureLevels:数组元素个数。

        (函数倒数第2个参数)

        __out_opt D3D_FEATURE_LEVEL* pFeatureLevel

       返回 pFeatureLevels 数组中第一个支持的特征等级
      (如果pFeatureLevels 为 null,则返回可支持的最高等级)。

老鸟:这样我们的大将就准备开拨了,但是他们之间并不熟悉,我们只是把他们全配置好了,但还没进行交互。

所以他们就有了第一次碰面,那是在军营的后台

ID3D11RenderTargetView* md3dRenderTargetView; //D3D11渲染目标视图

//--------------------------------------------------------------
//创建后台缓存视图
//--------------------------------------------------------------
ID3D11Texture2D*backBuffer;
ID3D11RenderTargetView* md3dRenderTargetView; //D3D11渲染目标视图

md3dSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer));

md3dDevice->CreateRenderTargetView(backBuffer, 0, &md3dRenderTargetView);
ReleaseCOM(backBuffer);

官方解释:资源不能被直接绑定到一个管线阶段;我们必须为资源创建资源视图,然后把资源视图

绑定到不同的管线阶段。尤其是在把后台缓冲区绑定到管线的输出合并器阶段时

(使 Direct3D 可以在后台缓冲区上执行渲染工作),

我们必须为后台缓冲区创建一个渲染目标视图(render target view) )。

老鸟:第一次只是简单会面,第二次就是要商谈行军布阵事宜,更深一步的交流:

ID3D11DepthStencilView* md3dDepthStencilView; //D3D11深度(模板)视图

        //--------------------------------------------------------------
    //填充2DTexture深度缓存(模板缓存)形容结构体,创建深度缓存(模板缓存)
    //--------------------------------------------------------------
    D3D11_TEXTURE2D_DESC depthStencilDesc;

    ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
    depthStencilDesc.Width = SCREEN_WIDTH;   //纹理的宽度,单位为纹理元素(texel)。
    depthStencilDesc.Height = SCREEN_HEIGHT; //纹理的高度,单位为纹理元素(texel)。
    depthStencilDesc.MipLevels = 1;          //多级渐近纹理层(mipmap level)的数量。
    depthStencilDesc.ArraySize = 1;          //在纹理数组中的纹理数量。
    depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;//一个DXGI_FORMAT枚举类型成员,
                                                               //它指定了纹理元素的格式。
    depthStencilDesc.SampleDesc.Count = 1;
    depthStencilDesc.SampleDesc.Quality = 0;
    depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;           //表示纹理用途的 D3D11_USAGE 枚举类型成员。
    depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;  //指定该资源将会绑定到管线的哪个阶段。
    depthStencilDesc.CPUAccessFlags = 0;                    //指定 CPU 对资源的访问权限。
    depthStencilDesc.MiscFlags = 0;                         //可选的标志值,与深度/模板缓冲区无关,所以设为 0。

    ID3D11Texture2D* md3dDepthStencilBuffer; //D3D11的“DepthStencil缓存”
    ID3D11DepthStencilView* md3dDepthStencilView; //D3D11深度(模板)视图

    HR(md3dDevice->CreateTexture2D(
                &depthStencilDesc,//已创建好的纹理的结构体
        0,&md3dDepthStencilBuffer)); //指向深度缓存的指针

        HR(md3dDevice->CreateDepthStencilView(
        md3dDepthStencilBuffer, //我们基于这个深度模板缓存创建一个视图
        0,&md3dDepthStencilView));//指向深度模板缓存的指针

老鸟:接下来就是通力合作:

    //把那些视图绑定到输出合并阶段

    md3dImmediateContext->OMSetRenderTargets(1, &md3dRenderTargetView, md3dDepthStencilView);
        //第一个参数是我们将要绑定的渲染目标的数量;

       ”沆瀣一气“,成为一个人,

       我们这里只描述其眼睛(由智囊团指挥):D3D11_VIEWPORT viewport;

    //第十,创建并设定视口

    D3D11_VIEWPORT viewport;
    viewport.Width = static_cast<float>(SCREEN_WIDTH);
    viewport.Height = static_cast<float>(SCREEN_HEIGHT);
    viewport.MinDepth = 0.0f;  //MinDepth 表示深度缓冲区的最小值(0),
    viewport.MaxDepth = 1.0f;  //MaxDepth 表示深度缓冲区的最大值(1)。
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;
    md3dImmediateContext->RSSetViewports(1, &viewport);

老鸟:最后便是摆阵,蓄势以待:

float color[4];

    //设置清除缓存(backbuffer),设置每帧初始颜色
    color[0] = 0.0f;
    color[1] = 0.5f;
    color[2] = 0.5f;
    color[3] = 1.0f;

    //用color清除背后缓存
    md3dImmediateContext->ClearRenderTargetView(md3dRenderTargetView, color);

    //清除深度缓存和模板缓存,设置每帧初始值
    md3dImmediateContext->ClearDepthStencilView(md3dDepthStencilView,
        D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    //因为渲染已经完成,提交backbuffer到屏幕

     bool mVsyncEnable = true;  //是否限帧渲染
    if (mVsyncEnable)
    {
        //限帧提交
        md3dSwapChain->Present(1, 0);
    }
    else
    {
        //尽可能快提交
        md3dSwapChain->Present(0, 0);
    }

老鸟:至此,我们dx11军形就摆好了,无论应对什么样的战役我们都可以应对了,

           小白,检验一下你的学习态度,就由你做个总结吧。

小白:yes,sir。

          DX基本框架流程:(1)声明大将与兵马;

                                         (2)准备好粮草;

                                         (3)配置好兵马;

                                         (4)大将与兵马合并;

                                         (5)大将在后台军营第一次正式见面;

                                         (6)大将之间深入详谈;

                                          (7)通力合作,观察敌情,摆阵。

老鸟:呀,总结的这么好,看来你学的很不错,孺子可教也。

铃铃铃(我们一起学猫叫,一起喵喵喵),小白的电话响了,

小白:哦哦,5黑,好,我马上来。 (挂掉电话) 

小白:哥,我同学叫我玩游戏去了,我先走了,嘻嘻,我要让他们看看我的神级刀妹。

源码链接:https://pan.baidu.com/s/1h7Zt_1bPAbgKpXdQzn8eYA

下节我们将以D3D11龙书的类组织形式回顾代码。

当你打算放弃梦想的时候,告诉自己再多撑一天,一个星期,一个月,再多撑一年吧。

等到繁华落尽的时候,你会发现,拒绝退场的结果,是一种怎样的美好。(转自浅墨csdn)

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章