必须吐槽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)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章