DirectX的初始化步骤较为繁琐,这里将DX的初始化扔进类的初始化(构造函数)里(如果构造函数里还要执行其他逻辑,那就最好把D3D的初始化扔进一个内联函数,这样能够更好地区分逻辑),下次要使用的时候,就直接继承该类,然后在这基础上写自己的东西
首先准备一个AppPublic.h,包含了以后可能要用到的所有头文件
#pragma once
#ifndef _APP_PUBLIC_
#define _APP_PUBLIC_
#include
#include
// D3D 相关头文件
#include
#include
#include
#endif
然后准备一个D3DApp.h
#pragma once
#ifndef _D3D_APP_
#define _D3D_AOP_
class D3DApp;
#include "AppPublic.h"
class D3DApp
{
protected:
HINSTANCE m_hInstance;
HWND m_hWnd;
UINT m_uWndWidth;
UINT m_uWndHeight;
//
// D3D 成员
//
D3D_FEATURE_LEVEL m_featureLevel;
D3D_DRIVER_TYPE m_driverType;
ID3D11Device* m_pD3dDevice;
ID3D11DeviceContext* m_pImmediateContext;
IDXGISwapChain* m_pSwapChain;
ID3D11RenderTargetView* m_pRenderTargetView;
ID3D11Texture2D* m_pDepthStencil;
ID3D11DepthStencilView* m_pDepthStencilView;
public:
D3DApp(HINSTANCE hInstance, HWND hwnd);
virtual ~D3DApp();
//
// 更新和渲染逻辑
//
virtual void Update();
virtual void Render();
//
// 键盘事件
//
virtual void OnKeyChange(UINT keyCode, BOOL isDown);
public:
static const float _bg_color[];
};
#endif
函数的分工如下,
接下来是实现(D3DApp.cpp)
#include "D3DApp.h"
const float D3DApp::_bg_color[] = { 0.0f, 0.2f, 0.0f, 1.0f };
D3DApp::D3DApp(HINSTANCE hInstance, HWND hwnd)
:m_hInstance(hInstance), m_hWnd(hwnd)
{
HRESULT hr;
m_hInstance = m_hInstance;
m_hWnd = m_hWnd;
RECT rc;
GetClientRect(m_hWnd, &rc);
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
m_uWndWidth = width;
m_uWndHeight = height;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE
};
UINT numDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
DXGI_SWAP_CHAIN_DESC sd{ 0 };
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = m_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Windowed = true;
UINT createDeviceFlags = 0;
for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; ++driverTypeIndex)
{
hr = D3D11CreateDeviceAndSwapChain(
nullptr,
driverTypes[driverTypeIndex],
nullptr,
createDeviceFlags,
featureLevels,
numFeatureLevels,
D3D11_SDK_VERSION,
&sd,
&m_pSwapChain,
&m_pD3dDevice,
&m_featureLevel,
&m_pImmediateContext
);
if (SUCCEEDED(hr))
{
m_driverType = driverTypes[driverTypeIndex];
break;
}
}
if (FAILED(hr))
EXCEPT("error at D3D11CreateDeviceAndSwapChain()");
// 创建并绑定 RenderTargetView
ID3D11Texture2D *pBackBuffer = NULL;
hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
if (FAILED(hr))
EXCEPT("error at m_pSwapChain->GetBuffer()");
hr = m_pD3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &m_pRenderTargetView);
if (pBackBuffer)
{
pBackBuffer->Release();
}
if (FAILED(hr))
EXCEPT("error at CreateRenderTargetView()");
D3D11_TEXTURE2D_DESC descDepth;
ZeroMemory(&descDepth, sizeof(descDepth));
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = m_pD3dDevice->CreateTexture2D(&descDepth, nullptr, &m_pDepthStencil);
if (FAILED(hr))
EXCEPT("error at m_pD3dDevice->CreateTexture2D()");
// 创建并绑定 DepthStencilView
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
ZeroMemory(&descDSV, sizeof(descDSV));
descDSV.Format = descDepth.Format;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
hr = m_pD3dDevice->CreateDepthStencilView(m_pDepthStencil, &descDSV, &m_pDepthStencilView);
if (FAILED(hr))
EXCEPT("error at CreateDepthStencilView()");
m_pImmediateContext->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);
// 初始化并设置 ViewPort
D3D11_VIEWPORT vp;
vp.Height = static_cast(height);
vp.Width = static_cast(width);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
m_pImmediateContext->RSSetViewports(1, &vp);
}
D3DApp::~D3DApp()
{
//
// 释放 D3D 资源
//
if (m_pSwapChain) m_pSwapChain->Release();
if (m_pImmediateContext) m_pImmediateContext->Release();
if (m_pRenderTargetView) m_pRenderTargetView->Release();
if (m_pDepthStencil) m_pDepthStencil->Release();
if (m_pDepthStencilView) m_pDepthStencilView->Release();
if (m_pD3dDevice) m_pD3dDevice->Release();
}
void D3DApp::Update()
{
// 暂时不包含任何更新逻辑
}
void D3DApp::Render()
{
// 仅做清屏一件事
if (!m_pImmediateContext) return;
m_pImmediateContext->ClearRenderTargetView(m_pRenderTargetView, D3DApp::_bg_color);
m_pImmediateContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
m_pSwapChain->Present(0, 0);
}
VOID D3DApp::OnKeyChange(UINT keyCode, BOOL isDown)
{
// 暂时不处理任何键盘事件
}
测试
#include "AppPublic.h"
#include "D3DApp.h"
constexpr SIZE SZ_WND = { 640, 480 };
D3DApp* g_pD3DApp;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID FixedWindowsSizeAndPosition(HWND hwnd);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
#ifdef _DEBUG
AllocConsole();
freopen("CONOUT$", "w", stdout);
#endif
LPCSTR CLASS_NAME = "D3DCarAppClass";
LPCSTR WIN_TITLE = "D3DCarApp - Yotwei";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
WIN_TITLE,
WS_OVERLAPPEDWINDOW ^ WS_SIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, SZ_WND.cx, SZ_WND.cy,
nullptr,
nullptr,
hInstance,
nullptr
);
if (nullptr == hwnd)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
FixedWindowsSizeAndPosition(hwnd);
g_pD3DApp = new D3DApp(hInstance, hwnd);
//g_pD3DApp = new D3DApp(hInstance, hwnd);
if (nullptr == g_pD3DApp)
return -1;
MSG msg = { };
while (WM_QUIT != msg.message) // 消息循环
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
g_pD3DApp->Update();
g_pD3DApp->Render();
}
}
delete g_pD3DApp; // 释放内存,会执行析构函数,然后释放里面的D3D资源
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
g_pD3DApp->OnKeyChange(wParam, TRUE);
break;
case WM_KEYUP:
g_pD3DApp->OnKeyChange(wParam, FALSE);
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
VOID FixedWindowsSizeAndPosition(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
UINT oldWidth = rc.right - rc.left;
UINT oldHeight = rc.bottom - rc.top;
UINT width = (SZ_WND.cx << 1) - oldWidth; // 设置窗口(不含边框)的区域尺寸为 SZ_WND
UINT height = (SZ_WND.cy << 1) - oldHeight;
UINT sw = GetSystemMetrics(SM_CXSCREEN);
UINT sh = GetSystemMetrics(SM_CYSCREEN);
UINT offsetX = (sw - width) >> 1; // 计算 x, y 偏移,让窗口居中
UINT offsetY = (sh - height) >> 1;
SetWindowPos(hwnd, nullptr, offsetX, offsetY, width, height, 0);
}
子类继承这个类的时候,每次子类的初始化就会调用父类的初始化,进而初始化D3D成员,子类只需要重写Update(),Render()里面的逻辑即可