【DirectX11学习01】用一个类封装DirectX的初始化

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

函数的分工如下,

  1. 构造函数负责初始化D3D成员
  2. 如果类使用完毕,析构函数就负责销毁D3D成员
  3. Update()函数负责执行更新逻辑
  4. Render()则负责渲染,绘制
  5. OnKeyChange()用于监听键盘事件

接下来是实现(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()里面的逻辑即可

你可能感兴趣的:(c++,DirectX,面向对象)