03.初始化Direct3D11
在这里我们将学习如何设置direct3d设备,并使用direct3d渲染到屏幕上!由于这是directx工作的最基础操作,从这一点开始的所有课程都将使用它,有时会在这里或那里进行一点修改。
本节自带的DEMO的下载地址为:
- https://www.braynzarsoft.net/file/3
- /uploadimg/braynzarsoft-d3d11/DX11_Lesson_03_D3D11_Initialization_zip.zip
在这里,我们声名全局变量。
第一个声明是COM接口对象。这是我们的SwapChain,用于将后缓冲区更改为前缓冲区,并将前缓冲区改为后缓冲区。这被称为双缓冲。当我们渲染场景时,我们正在后台缓冲区渲染,所以当我们将后台缓冲区呈现给监视器时,它将被完全绘制出来。否则,我们将得到扫描线,这时我们可以看到我们的程序将我们的场景绘制到屏幕上的时候,通常是从上到下。
使用双缓冲区的好处是一次在后台渲染,然后翻转到前台。否则直接在前台渲染,可以看到从上下到的渲染过程,会出现扫描线或者闪屏。
接下来是一个接口,我们将使用它来表示我们的硬件设备(GPU)。现在,在那之后的一行,是directx 11附带的一个新接口。ID3D11Device接口被一分为二,以帮助支持新的多线程功能。我们将使用我们的ID3D11DeviceContext接口对象来调用所有的渲染方法,而ID3D11Device将用于调用其余与渲染无关的方法。
其实我以前还在奇怪,ID3D11DeviceContext和ID3D11Device应该是一样的,为什么要分成两个接口。这里就说明的很清楚了。是为了多线程的支持。ID3D11Device用于与渲染无关的一些接口操作,而ID3D11DeviceContext接口是与渲染线程相关的。
我将更好地解释将ID3D11Device一分为二的原因。DirectX 11具有新的多线程功能,用于加快应用程序的速度。当您将一些东西加载到内存中时,例如模型或创建对象,您将调用ID3D11Device对象。在加载或创建对象或模型时,可以调用ID3D11DeviceContext接口对象以继续渲染场景。这将减少加载模型、创建对象或类似操作时的性能冲击。这不是一个难以理解的想法,所以我希望你能听懂我的话。
纹理等资源加载到内存,可以不影响渲染相关的。
因此,在那之后,我们有了另一个界口对象(interface object,),即渲染目标视图。基本上,我们不直接写入屏幕,而是写入渲染目标视图,这是一个2d纹理(我们的后缓冲区)。然后,将此纹理作为渲染目标发送到管道的输出合并阶段,然后将其渲染到屏幕上。
接下来的6行用于更改背景的颜色,这对本课程并不重要。
IDXGISwapChain* SwapChain;
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11RenderTargetView* renderTargetView;
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
int colormodr = 1;
int colormodg = 1;
int colormodb = 1;
接下来我们声明我们的函数原型。第一个函数用于初始化direct3d。第二个是释放我们不需要的对象,以防止内存泄漏。
InitScene用于设置或场景。更新场景用于在每帧的基础上更改我们的场景,然后绘制场景用于将我们的场景绘制到屏幕上,并且也会在每帧更新。
bool InitializeDirect3d11App(HINSTANCE hInstance);
void ReleaseObjects();
bool InitScene();
void UpdateScene();//更新场景
void DrawScene(); //绘制场景
在我们的winMain函数中,我们将调用InitializeDirect3d11App,然后调用InitScene。之后,我们将调用消息循环,当消息循环完成时,我们释放对象,然后结束程序。
if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D
{
MessageBox(0, L"Direct3D Initialization - Failed",L"Error", MB_OK);
return 0;
}
if(!InitScene()) //Initialize our scene
{
MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK);
return 0;
}
//messageloop中无消息时分别调用UpdateScene用于更新场景和DrawScene用于绘制场景。
messageloop();
ReleaseObjects();
初始化Direct3D 11
初始化函数direct3d有一个参数,即我们的应用程序实例的句柄(instance)。
bool InitializeDirect3dApp(HINSTANCE hInstance)
{
HRESULT hr;
//Describe our Buffer
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//Describe our SwapChain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//Create our SwapChain
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
//Create our BackBuffer
ID3D11Texture2D* BackBuffer;
hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );
//Create our Render Target
hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView );
BackBuffer->Release();
//Set our Render Target
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );
return true;
}
首先,我们创建一个名为hr的HRESULT对象,用于错误检查。为了使代码更加清晰和简洁,我没有包括错误检查,但我将在本课结束时解释如何实现错误检查。
HRESULT hr;
背景缓冲区描述符DXGI_MODE_DESC
背景缓冲区-背景,就是要在后台渲染的
在这个函数中,我们要做的第一件事是描述我们的后台缓冲区。我们创建一个名为bufferDesc的DXGI_MODE_DESC对象。然后我们调用ZeroMemory以确保对象被完全清除(以防我们没有设置所有参数,并且参数中已经有值)。然后我们填写我们的后台缓冲区描述。
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, *LPDXGI_MODE_DESC;
- Width 这是我们将要使用的分辨率的宽度。
- Height这是我们要使用的分辨率的高度。
- RefreshRate 这是DXGI_RATIONAL类型,以赫兹为单位描述刷新率。我们将我们的频率设置为60/1或60hz
- Format 这是一个DXGI_FORMAT枚举类型,用于描述我们的显示格式。我们可以使用DXGI_FORMAT_R8G8B8_UNORM,这是一个32位无符号整数,红色、绿色、蓝色和Alpha各取8位。
- ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER枚举类型,描述光栅化器渲染到曲面上的方式。由于我们使用双重缓冲,通常不会看到这种情况,因此我们可以将其设置为DXGI_MODE_SCANLINE_ORDER_UNSECIFIED,这意味着在曲面上进行渲染的顺序无关紧要。
- Scaling :DXGI_MODE_SCALING,解释如何拉伸图像以适应监视器的分辨率。我们可以使用以下三种中的一种:
- DXGI_MODE_SCALING_UNSPECIFIED,这意味着它没有被指定;
- DXGI_MODE_SCALING_CENTERED,意味着图像位于屏幕的中心,根本不进行缩放和拉伸;
- DXGI_Model_SCALING_STRETCHED,它将图像拉伸到监视器的分辨率。
代码如下:
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
交换链描述符DXGI_SWAP_CHAIN_DESC
现在我们已经填写了后台缓冲区描述,我们可以继续描述SwapChain了。我们创建一个名为swapChainDesc的DXGI_SWAP_CHAIN_DESC,然后通过调用ZeroMemory函数将其清除。之后,我们可以填写描述。结构如下所示:
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;
- BufferDesc:这是一个DXGI_MODE_DESC结构,用于描述后台缓冲区。我们将把刚才填写的bufferDesc对象放在这里。
- SampleDesc:这是一个DXGI_SAMPLE_DESC结构,用于描述多重采样。如果你不知道,多重采样是用来“平滑”线条和边缘的起伏,这是因为监视器上的像素不是无限小。由于像素就像小块,你可以在电脑屏幕上看到对角线和边缘的“起伏”。说白了就是为了抗锯齿
- BufferUsage:DXGI_USAGE枚举类型,描述cpu对后缓冲区表面的访问。我们指定DXGI_USAGE_RENDER_TARGET_OUTPUT,因为我们将对其进行渲染。
- BufferCount:这是我们将使用的后台缓冲区的数量。我们为双缓冲设置了1,但您可以为三缓冲设置2,如果您愿意,甚至可以设置更多。
- OutputWindow:输出窗口的句柄。
- Windowed:这要么是真的,要么是假的,这取决于我们是想要窗口化的还是全屏的。为窗口设置true,为全屏设置false。(退出全屏时要小心。这可能会冻结你的程序。解决方法是在你真正退出程序之前将应用程序设置为窗口)
- SwapEffect:这是一个DXGI_SWAP_EFFECT枚举类型,描述了将前缓冲区交换到后缓冲区后,显示驱动程序应该如何处理前端缓冲区。
- Flags:DXGI_SWAP_CHAIN_FLAG枚举类型。这是一个描述交换链行为的额外标志。目前唯一可用的是DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,它可以在窗口和全屏之间切换时更改监视器的分辨率。
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;//交换后,丢掉前端缓冲区内容
创建Device和SwapChain
接下来,我们通过调用direct3d核心函数D3D11CreateDeviceAndSwapChain()来创建direct3d设备、设备上下文和交换链。函数参数如下所示:
HRESULT D3D11CreateDeviceAndSwapChain(
__in IDXGIAdapter *pAdapter,
__in D3D_DRIVER_TYPE DriverType,
__in HMODULE Software,
__in UINT Flags,
__in const D3D_FEATURE_LEVEL *pFeatureLevels,
__in UINT FeatureLevels,
__in UINT SDKVersion,
__in const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
__out IDXGISwapChain **ppSwapChain,
__out ID3D11Device **ppDevice,
__out D3D_FEATURE_LEVEL *pFeatureLevel,
__out ID3D11DeviceContext **ppImmediateContext
);
参数如下:
- pAdapter:这是指向要使用的视频适配器的指针。我们可以将NULL设置为使用默认值表示使用主显卡。系统有多张显卡时,可以使用这个变量来指定使用那张显卡。
- DriverType:D3D_DRIVER_TYPE枚举类型。这说明了direct3d将使用何种方式来实现。我们使用D3D_DRIVER_TYPE_HARDWARE来表示direct3d将由GPU(图形卡)实现。
- Software:这是一个用于实现软件光栅化的DLL的HMODULE句柄。
- Flags:这是一个或多个D3D11_CREATE_DEVICE_FLAG类型或组合在一起。
- pFeatureLevels:这是一个指向D3D_FEATURE_LEVEL枚举类型数组的指针,该数组表示要使用的directx功能的版本。将NULL设置为使用最高可用功能。
- FeatureLevels:这是pFeatureLevels数组中的个数。设置为NULL。
- SDKVersion:The version of the DirectX SDK. Use D3D11_SDK_VERSION
- pSwapChainDesc:指向我们在上面创建的DXGI_SWAP_CHAIN_DESC结构的指针。
- ppSwapChain:指向IDXGISwapChain接口的指针,用于接收创建的SwapChain。
- ppDevice:指向我们的direct3d设备的指针。用于与渲染线程无关的操作。
- pFeatureLevel:这是一个指向D3D_FEATURE_LEVEL的指针,它将保持可用的最高功能级别。(功能级别用于向后兼容性)
- ppImmediateContext:这是指向我们的ID3D11DeviceContext(设备上下文)的指针。请记住,设备上下文将用于设备的渲染方法,以支持多线程并提高性能。
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
创建背景缓冲区
接下来,我们创建我们的backbuffer,它将用于创建渲染目标视图。我们调用SwapChain接口的GetBuffer方法:
HRESULT GetBuffer(
[in] UINT Buffer,
[in] REFIID riid,
[in, out] void **ppSurface
);
参数如下:
- Buffer:因为我们设置了swapChainDesc。SwapEffect到DXGI_SWAP_EFECT_DISCARD,我们只能访问第一个缓冲区,所以我们设置为0。
- riid:这是用于更改后台缓冲区的接口类型的引用ID。我们使用2d纹理(ID3D11Texture2D)。
- ppSurface:这是一个指向我们在上面创建的BackBuffer的指针,它是我们将渲染到的曲面。
ID3D11Texture2D* BackBuffer;
hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );
创建渲染目标对象
D3D11做了封装抽象,要渲染的东西都必须是渲染目标对象。这些对象具体可以是纹理等。
现在我们创建渲染目标视图,将其发送到管道的输出合并阶段。我们可以通过调用设备接口的CreateRenderTargetView来创建渲染目标视图。
HRESULT CreateRenderTargetView(
[in] ID3D11Resource *pResource,
[in] const D3D11_RENDER_TARGET_VIEW_DESC *pDesc,
[out] ID3D11RenderTargetView **ppRTView
);
参数如下:
- pResource:背景缓冲区指针。创建渲染目标对象的输入源。
- pDesc:指向D3D11_RENDER_TARGET_VIEW_DESC结构的指针。我们将NULL设置为创建一个访问mipmap级别0中所有子资源的视图。
- ppRTView:返回值。这是指向我们的ID3D11RenderTargetView接口renderTargetView的指针。
现在我们不再需要后台缓冲区了,我们可以释放它。
hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView );
BackBuffer->Release();
设置渲染目标对象
创建的渲染目标对象需要绑定到输出端才可以输出显示。
初始化时我们要做的最后一件事是将渲染目标视图绑定到管道的输出合并阶段。这个函数也将绑定我们的深度/模板缓冲区,但我们还没有创建一个深度/模板缓冲区,所以我们将该参数设置为NULL。
void OMSetRenderTargets(
[in] UINT NumViews,
[in] ID3D11RenderTargetView *const **ppRenderTargetViews,
[in] ID3D11DepthStencilView *pDepthStencilView
);
参数如下:
- NumViews:这是要绑定的渲染目标数。我们只有一个。
- ppRenderTargetViews:这是要绑定的渲染目标视图的数组。
- pDepthStencilView:这是一个指向深度模具缓冲区的指针。我们还没有,所以我们将这个设置为NULL。
direct3d 11的基本初始化就到此为止。
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );
清理
接下来我们有其他新功能。第一个是ReleaseObjects),它将释放我们创建的所有COM对象。不要忘记这样做,否则会造成内存泄漏。
void ReleaseObjects()
{
//Release the COM Objects we created
SwapChain->Release();
d3d11Device->Release();
d3dDevCon->Release();
}
初始化场景
InitScene()函数将用于初始化我们的场景。在电子游戏中,您可能会有许多不同的场景,因此您可能需要从InitScene()重命名它们。我们将放置我们的对象,加载我们的模型、纹理、声音,所有这些都必须完成才能开始特定的场景。
这是obs场景的设计思路?到时看看大佬是怎么一步一步弄的。
bool InitScene()
{
return true;
}
更新场景
接下来我们有UpdateScene()函数。我们将使用此功能来完成场景的所有更新,如更改对象位置、更改值,场景中的任何更改都将在此处完成。在本课中,我们将只更改背景的颜色,因此在更新场景功能中,我们只更改颜色。
void UpdateScene()
{
//Update the colors of our scene
red += colormodr * 0.00005f;
green += colormodg * 0.00002f;
blue += colormodb * 0.00001f;
if(red >= 1.0f || red <= 0.0f)
colormodr *= -1;
if(green >= 1.0f || green <= 0.0f)
colormodg *= -1;
if(blue >= 1.0f || blue <= 0.0f)
colormodb *= -1;
}
渲染场景
现在我们有了DrawScene()函数。这是我们将简单地渲染场景的地方。我们应该避免在这个场景中进行任何更新,并保留这个功能仅用于绘制我们的场景。这是我们将改变背景颜色的地方。请记住,如果您来自DirectX的早期版本,如DirectX 10,则所有渲染方法都已移动到设备上下文接口,因此,我们将调用d3dDeviceContext对象以获取ClearRenderTargetView方法,而不是像在DirectX 10中那样调用d3dDevice,因为这是一种渲染方法。除了渲染之外,我们将调用d3dDevice来处理与GPU有关的其他事情。最后,我们通过调用swapchain接口的present方法来呈现场景。这样做的目的是将前缓冲区与后缓冲区交换。在drawscene函数中,我们将渲染到backbuffer,然后在调用Present方法时显示backbuffer。
void DrawScene()
{
//Clear our backbuffer to the updated color
D3DXCOLOR bgColor( red, green, blue, 1.0f );
d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);
//Present the backbuffer to the screen
SwapChain->Present(0, 0);
}
这是我们的messageloop()函数。这与我们在上一课中创建窗口时几乎相同,但现在当没有消息要检查时,我们将首先调用UpdateScene()函数来更新我们的场景,然后调用DrawScene(()函数,将我们的场景绘制到后台缓冲区,并将后台缓冲区显示到屏幕。
int messageloop(){
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while(true)
{
BOOL PeekMessageL(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else{
///////////////**************new**************////////////////////
// run game code
UpdateScene();
DrawScene();
///////////////**************new**************////////////////////
}
}
return msg.wParam;
}
错误检查
这将节省您在调用函数时检查代码中错误的时间,因为即使函数没有成功,应用程序也可以继续运行。当函数返回时,您可以检查HRESULT的值以了解如何继续您的代码。以下是HRESULT的返回值:
确定(_OK)
- S_OK:功能成功。
- E_NOTIMPL:该功能未实现。
- E_NOINTERFACE:不支持该接口。
- E_ABORT:该功能已中止。
- E_FAIL 函数失败。
- E_INVALIDARG:一个或多个参数无效
您可以通过调用DXGetErrorDescription(HRESULT-HRESULT)函数来显示返回的确切错误。如果要使用此函数,则需要链接到“DXErr.lib”库并包含“DXErr.h”标头。
以下是带有错误检查的InitializeDirect3d11App()函数:(当错误框弹出时,您需要确保您没有处于全屏状态。您可以在显示消息之前转到窗口模式来完成此操作。)
#pragma comment (lib, "DXErr.lib")
#include <DXErr.h>
...
bool InitializeDirect3d11App(HINSTANCE hInstance)
{
HRESULT hr;
//Describe our Buffer
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//Describe our SwapChain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//Create our SwapChain
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
if(FAILED(hr))
{
MessageBox(NULL, DXGetErrorDescription(hr),
TEXT(" D3D11CreateDeviceAndSwapChain"), MB_OK);
return 0;
}
//Create our BackBuffer
ID3D11Texture2D* BackBuffer;
hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );
if(FAILED(hr))
{
MessageBox(NULL, DXGetErrorDescription(hr),
TEXT("SwapChain->GetBuffer"), MB_OK);
return 0;
}
//Create our Render Target
hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView );
BackBuffer->Release();
if(FAILED(hr))
{
MessageBox(NULL, DXGetErrorDescription(hr),
TEXT("d3d11Device->CreateRenderTargetView"), MB_OK);
return 0;
}
//Set our Render Target
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );
return true;
}
完整的练习代码
在本课中,我们创建了一个双缓冲区。
以下是完整的来源:
///////////////**************new**************////////////////////
//Include and link appropriate libraries and headers//
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
#include <windows.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#include <xnamath.h>
//Global Declarations//
IDXGISwapChain* SwapChain;
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11RenderTargetView* renderTargetView;
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
int colormodr = 1;
int colormodg = 1;
int colormodb = 1;
///////////////**************new**************////////////////////
LPCTSTR WndClassName = L"firstwindow";
HWND hwnd = NULL;
const int Width = 300;
const int Height = 300;
///////////////**************new**************////////////////////
//Function Prototypes//
bool InitializeDirect3d11App(HINSTANCE hInstance);
void ReleaseObjects();
bool InitScene();
void UpdateScene();
void DrawScene();
///////////////**************new**************////////////////////
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed);
int messageloop();
LRESULT CALLBACK WndProc(HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, //Main windows function
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true))
{
MessageBox(0, L"Window Initialization - Failed",
L"Error", MB_OK);
return 0;
}
///////////////**************new**************////////////////////
if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D
{
MessageBox(0, L"Direct3D Initialization - Failed",
L"Error", MB_OK);
return 0;
}
if(!InitScene()) //Initialize our scene
{
MessageBox(0, L"Scene Initialization - Failed",
L"Error", MB_OK);
return 0;
}
messageloop();
ReleaseObjects();
///////////////**************new**************////////////////////
return 0;
}
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed)
{
typedef struct _WNDCLASS {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2);
wc.lpszMenuName = NULL;
wc.lpszClassName = WndClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Error registering class",
L"Error", MB_OK | MB_ICONERROR);
return 1;
}
hwnd = CreateWindowEx(
NULL,
WndClassName,
L"Window Title",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
NULL,
NULL,
hInstance,
NULL
);
if (!hwnd)
{
MessageBox(NULL, L"Error creating window",
L"Error", MB_OK | MB_ICONERROR);
return 1;
}
ShowWindow(hwnd, ShowWnd);
UpdateWindow(hwnd);
return true;
}
///////////////**************new**************////////////////////
bool InitializeDirect3d11App(HINSTANCE hInstance)
{
//Describe our Buffer
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//Describe our SwapChain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//Create our SwapChain
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
//Create our BackBuffer
ID3D11Texture2D* BackBuffer;
SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );
//Create our Render Target
d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView );
BackBuffer->Release();
//Set our Render Target
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );
return true;
}
void ReleaseObjects()
{
//Release the COM Objects we created
SwapChain->Release();
d3d11Device->Release();
d3d11DevCon->Release();
}
bool InitScene()
{
return true;
}
void UpdateScene()
{
//Update the colors of our scene
red += colormodr * 0.00005f;
green += colormodg * 0.00002f;
blue += colormodb * 0.00001f;
if(red >= 1.0f || red <= 0.0f)
colormodr *= -1;
if(green >= 1.0f || green <= 0.0f)
colormodg *= -1;
if(blue >= 1.0f || blue <= 0.0f)
colormodb *= -1;
}
void DrawScene()
{
//Clear our backbuffer to the updated color
D3DXCOLOR bgColor( red, green, blue, 1.0f );
d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);
//Present the backbuffer to the screen
SwapChain->Present(0, 0);
}
///////////////**************new**************////////////////////
int messageloop(){
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while(true)
{
BOOL PeekMessageL(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else{
///////////////**************new**************////////////////////
// run game code
UpdateScene();
DrawScene();
///////////////**************new**************////////////////////
}
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch( msg )
{
case WM_KEYDOWN:
if( wParam == VK_ESCAPE ){
DestroyWindow(hwnd);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
}