上一讲我们主要介绍了一个基类HAppWindow,其实如果只想快速进入3D世界的话我们完全可以更加简单粗暴一些,之所以花了这么多时间费这么大的劲是因为我们希望在学习DirectX的时候顺便就把C++的编程思想也搞定。看过所有C++的语法但是还是搞不好一个系统这种情况比比皆是,所以我们希望在了解DirectX的时候也顺便来一起看看如何架构一个框架。
目前我们已经能够很快速的实现一个窗口,当然如果我们使用MFC或者Qt的话这些东西都可以忽略了,但是我们现在并没有使用Qt,同样没有使用MFC,就算我们现在使用Qt或者MFC,我们同样也会面临一个问题:比如很多人会问,如何在Qt里面使用DirectX呢?当然使用MFC的话会相对简单一些,但同样会存在问题,比如,事情的处理这些都是需要我们解决的。现在好了,我们设计得有通道的接口(HAppWindow),所以不管是在Qt或者MFC中,使用DirectX就变得很简单了。
说了一圈的废话,那么今天的主题是什么呢?在画出一个窗口后我们应该考虑做些什么吧,比如,我们可以开始对DirectX进行初始化啦,对于DirectX来说,我们可以准备一个基类,这个类我们就暂定为MDx11Base。
//==============================
class MDx11Base : public MNoCopy
{
public:
MDx11Base();
virtual ~MDx11Base();
virtual bool dx_Init(MAppWindow* container,bool isMultSample = true);
//初始化
void dx_Clear();
//清除资源
//
// 加载和卸载资源
//
virtual bool dx_LoadContent();
virtual bool dx_UnloadContent();
//
// 更新操作
//
virtual
void dx_Update(float dt);
//按时间更新
//
// 改变大小
//
virtual
void dx_ReShape(int width,
int height);
//
// 渲染函数
//
virtual
void dx_Render();
//渲染视图
//
// CPU空闲处理函数
//
virtual
void dx_OnIdle();
//
// 获取D3d相关信息
//
ID3D11Device* dx_GetDevice();
ID3D11DeviceContext* dx_GetContext();
IDXGISwapChain* dx_GetSwapChain();
ID3D11RenderTargetView* dx_GetRanderTargetView();
ID3D11Texture2D* dx_GetDepthBuffer();
ID3D11DepthStencilView* dx_GetDepthStencilView();
D3D11_VIEWPORT dx_GetViewPort();
private:
HINSTANCE m_hInstance;
//实例句柄
HWND
m_hwnd;
//窗口句柄
D3D_DRIVER_TYPE m_driverType;
//驱动类型
D3D_FEATURE_LEVEL m_featureLevel;
ID3D11Device* p_d3dDevice;
ID3D11DeviceContext* p_d3dContext;
//渲染环境
IDXGISwapChain* p_swapChain;
//交换链
ID3D11RenderTargetView* p_backBufferTarget;
//渲染目标视图
//
// 深度模板资源
//
ID3D11Texture2D* p_depthTexture{nullptr };
ID3D11DepthStencilView* p_depthStencilView{nullptr };
//
// 视口信息
//
D3D11_VIEWPORT m_viewport;
//
// 是否启用多重采样
//
bool b_is_Enable4xMsaa{ true
};
bool b_is_UsePreBuidFont{ true
};
unsigned m_4xMsaaQuality{0
};
};
//========================================
如大家所见,这个基类的东西都相当简单,所以我们重点来看看dx_Init这个函数,这是重点。dx_Render 负责3D模型的渲染,dx_Update 可以简单的实现动画效果。dx_ReShape 当窗口大小改变时会被调用。好吧,我们接下来实现这个基类。
//=======================================
MDx11Base::MDx11Base():
m_driverType(D3D_DRIVER_TYPE_NULL),
m_featureLevel(D3D_FEATURE_LEVEL_11_0),
p_d3dDevice(0),
p_d3dContext(0),
p_swapChain(0),
p_backBufferTarget(0),
p_depthTexture(nullptr),
p_depthStencilView(nullptr)
{
}
MDx11Base::~MDx11Base()
{
dx_Clear();
}
bool MDx11Base::dx_Init(MAppWindow* container, bool isMultSample){
if (container == nullptr || container->WindHwnd() == nullptr)
return false;
m_hwnd = container->WindHwnd();
b_is_UsePreBuidFont = isPreBuidFont;
pContainerWindow = container;
m_hInstance = GetModuleHandle(nullptr);
b_is_Enable4xMsaa = isMultSample;
RECT dimensions;
GetClientRect(m_hwnd, &
dimensions);
unsigned
int width = dimensions.right – dimensions.left;
unsigned
int height = dimensions.bottom – dimensions.top;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_SOFTWARE
};
unsigned
int totalDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
unsigned
int totalFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL myFeatureLevel;
HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, 0, 0,
featureLevels, 3, D3D11_SDK_VERSION, &
p_d3dDevice, &
myFeatureLevel, &
p_d3dContext);
if (FAILED(hr))
{
box::ErrorBox(“创建d3d11设备失败!”, m_hwnd);
return false;
}
if (myFeatureLevel != D3D_FEATURE_LEVEL_11_0)
{
if (IDNO == box::InfoBox(“您的机器不支持D3D11全部功能n程序运行可能会出问题,继续吗?”, m_hwnd))
{
return false;
}
}
//
// 检测4x采样等级
//
p_d3dDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, &
m_4xMsaaQuality);
DXGI_SWAP_CHAIN_DESC swapChainDesc ={ 0
};
swapChainDesc.BufferDesc.Width = width;
//宽、高
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
//刷新率
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//恒定参数,按照这样指明即可
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
//同上
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
//数据格式,一个为RGBA四元色格式
swapChainDesc.BufferCount = 1;
//后缓冲区个数,1个足够
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
//Usage,很好理解
swapChainDesc.Flags = 0;
swapChainDesc.OutputWindow = m_hwnd;
//主窗口句柄
if (b_is_Enable4xMsaa){
swapChainDesc.SampleDesc.Count = 4;
//多重采样
swapChainDesc.SampleDesc.Quality = m_4xMsaaQuality – 1;
}
else{
swapChainDesc.SampleDesc.Count = 1;
//多重采样
swapChainDesc.SampleDesc.Quality = 0;
}
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//交换:绝大多数情况用DISCARD
swapChainDesc.Windowed = true;
//窗口模式
unsigned
int creationFlags = 0;
#ifdef _DEBUG
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT result;
unsigned
int driver = 0;
//
//获取IDXGIFactory以创建交换链
//
IDXGIDevice *dxgiDevice(NULL);
p_d3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&
dxgiDevice));
IDXGIAdapter *dxgiAdapter(NULL);
dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&
dxgiAdapter));
IDXGIFactory *dxgiFactory(NULL);
dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&
dxgiFactory));
result = dxgiFactory->CreateSwapChain(p_d3dDevice, &
swapChainDesc, &
p_swapChain);
if (FAILED(hr))
{
MDx11String str = MDx11String::Format(“Failed to create the Direct3D SwapchainnFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
dxgiFactory->Release();
dxgiAdapter->Release();
dxgiDevice->Release();
if (FAILED(result))
{
MDx11String str = MDx11String::Format(“Failed to create the Direct3D devicenFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
ID3D11Texture2D* backBufferTexture;
result = p_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&
backBufferTexture);
if (FAILED(result))
{
MDx11String str = MDx11String::Format(“Failed to get the swap chain back buffernFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
result = p_d3dDevice->CreateRenderTargetView(backBufferTexture, 0, &
p_backBufferTarget);
if (backBufferTexture)
backBufferTexture->Release();
if (FAILED(result))
{
MDx11String str = MDx11String::Format(“Failed to create the render target viewnFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
//
// 创建深度模板缓存
//
D3D11_TEXTURE2D_DESC depth_desc;
ZeroMemory(&
depth_desc, sizeof(depth_desc));
depth_desc.Width = width;
depth_desc.Height = height;
depth_desc.MipLevels = 1;
depth_desc.ArraySize = 1;
depth_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depth_desc.SampleDesc.Count = 1;
depth_desc.SampleDesc.Quality = 0;
depth_desc.Usage = D3D11_USAGE_DEFAULT;
depth_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depth_desc.CPUAccessFlags = 0;
depth_desc.MiscFlags = 0;
safe_release(p_depthTexture);
hr = p_d3dDevice->CreateTexture2D(&
depth_desc, nullptr, &
p_depthTexture);
if (FAILED(hr)){
MDx11String str = MDx11String::Format(“CreateTexture2D FailnFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc;
ZeroMemory(&
dsv_desc, sizeof(dsv_desc));
dsv_desc.Format = depth_desc.Format;
dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsv_desc.Texture2D.MipSlice = 0;
safe_release(p_depthStencilView);
hr = p_d3dDevice->CreateDepthStencilView(p_depthTexture, &
dsv_desc, &
p_depthStencilView);
if (FAILED(hr)){
MDx11String str = MDx11String::Format(“CreateDepthStencilView FailnFILE:%1nFun:%2nLine:%3”, __FILE__, __FUNCTION__, __LINE__);
box::ErrorBox(str, m_hwnd);
return false;
}
p_d3dContext->OMSetRenderTargets(1, &
p_backBufferTarget, p_depthStencilView);
//p_depthStencilView
m_viewport.Width = static_cast<
float>(width);
m_viewport.Height = static_cast<
float>(height);
m_viewport.MinDepth = 0.0f;
m_viewport.MaxDepth = 1.0f;
m_viewport.TopLeftX = 0.0f;
m_viewport.TopLeftY = 0.0f;
p_d3dContext->RSSetViewports(1, &
m_viewport);
//
// 绑定必要的相关函数
//
pContainerWindow->RegisterOnIdleFun(MSLOT(&
MDx11Base::dx_Render, this));
pContainerWindow->RegisterDrawScreen(MSLOT(&
MDx11Base::dx_Render, this));
pContainerWindow->RegisterReShape(MSLOT(&
MDx11Base::dx_ReShape, this));
pContainerWindow->RegisterUpdataFun(MSLOT(&
MDx11Base::dx_Update, this));
pContainerWindow->RegisterClearFun(MSLOT(&
MDx11Base::dx_Clear, this));
pContainerWindow->RegisterMouseEnventFun(MSLOT(&
MDx11Base::dx_MouseEvent, this));
pContainerWindow->RegisterKeyEventFun(MSLOT(&
MDx11Base::dx_KeyEvent, this));
//
// 加载资源
//
return dx_LoadContent();
}
void MDx11Base::dx_Clear(){
dx_UnloadContent();
safe_release(p_depthStencilView);
safe_release(p_depthTexture);
safe_release(p_backBufferTarget);
safe_release(p_swapChain);
safe_release(p_d3dContext);
safe_release(p_d3dDevice );
safe_delete(pBufferManagerPtr);
}
bool MDx11Base::dx_LoadContent(){
return true;
}
bool MDx11Base::dx_UnloadContent(){
return true;
}
void MDx11Base::dx_Update(float dt){
//
重载的时候重写该函数,如有必要的话
}
void MDx11Base::dx_ReShape(int width,
int height){
assert(p_d3dContext);
assert(p_d3dDevice);
assert(p_swapChain);
safe_release(p_backBufferTarget);
safe_release(p_depthStencilView);
safe_release(p_depthTexture);
HRESULT hr = p_swapChain->ResizeBuffers(1, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
ID3D11Texture2D* backBuffer;
hr = p_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&
backBuffer));
hr = p_d3dDevice->CreateRenderTargetView(backBuffer, 0, &
p_backBufferTarget);
safe_release(backBuffer);
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = width;
depthStencilDesc.Height = height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
if (b_is_Enable4xMsaa)
{
depthStencilDesc.SampleDesc.Count = 4;
depthStencilDesc.SampleDesc.Quality = m_4xMsaaQuality – 1;
}
// No MSAA
else
{
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
}
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
hr = p_d3dDevice->CreateTexture2D(&
depthStencilDesc, 0, &
p_depthTexture);
hr = p_d3dDevice->CreateDepthStencilView(p_depthTexture, 0, &
p_depthStencilView);
p_d3dContext->OMSetRenderTargets(1, &
p_backBufferTarget, p_depthStencilView);
//
m_viewport.TopLeftX = 0;
m_viewport.TopLeftY = 0;
m_viewport.Width = static_cast<
float>(width);
m_viewport.Height = static_cast<
float>(height);
m_viewport.MinDepth = 0.0f;
m_viewport.MaxDepth = 1.0f;
p_d3dContext->RSSetViewports(1, &
m_viewport);
}
void MDx11Base::dx_Render(){
//
重载的时候重写该函数(必须)
}
void MDx11Base::dx_OnIdle(){
// 需要的时候可以重新写
}
//
// 获取D3d相关信息
//
ID3D11Device* MDx11Base::dx_GetDevice(){
return p_d3dDevice;
}
ID3D11DeviceContext* MDx11Base::dx_GetContext(){
return p_d3dContext;
}
IDXGISwapChain* MDx11Base::dx_GetSwapChain(){
return p_swapChain;
}
ID3D11RenderTargetView* MDx11Base::dx_GetRanderTargetView(){
return p_backBufferTarget;
}
ID3D11Texture2D* MDx11Base::dx_GetDepthBuffer(){
return p_depthTexture;
}
ID3D11DepthStencilView* MDx11Base::dx_GetDepthStencilView(){
return p_depthStencilView;
}
D3D11_VIEWPORT MDx11Base::dx_GetViewPort(){
return m_viewport;
}
//=========================
除了 dx_Init 和 dx_ReShape 两个函数之外其余的都没啥好说,其实这两个函数也没啥好说,如大家所见这是在初始化DirectX11 库的初始化,所以只要记住这么初始化就好了,嗯,确实是简单粗暴啊,现在我们只需要记住一点就好:如果我们有额外的需要初始化的东西可以放在dx_Init函数的后面,如果是在我们的框架中注定要有的东西可以这么干,如果是独立的初始化那么在子类的dx_Init函数中先调用父类的dx_Init函数,如果没问题了再对额外的东西进行初始化,当然为了更大的灵活性可以考虑不要在父类的dx_Init函数中调用dx_LoadContent(),具体调用由你决定,但事实上我们几乎不会去重写dx_Init这个函数,至于dx_ReShape几乎都会重写,但重写的时候记得调用MDx11Base::dx_ReShape。
现在有了这个基类之后,我们来实现我们的第一个DirectX11应用程序:
//======================
class FirstDirectWindow : public MDx11Base{
public:
FirstDirectWindow (){
}
~FirstDirectWindow (){ }
bool dx_LoadContent(){
return true;
}
void dx_Render();
};
void FirstDirectWindow ::dx_Render(){
if (!dx_GetDevice() || !dx_GetContext()){
return;
}
float clearColor[4] ={ 0.f, 0.f, 0.25f, 1.0f
};
p_d3dContext->ClearRenderTargetView(p_backBufferTarget, clearColor);
p_d3dContext->ClearDepthStencilView(p_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0.0f);
dx_GetSwapChain()->Present(0, 0);
}
//=========================
为了简单一点,所以我们什么都没有绘制,只是将窗口背景修改为蓝色,嗯,浅蓝色还是深蓝色有点搞不定了,总之是rgb的b分量为0.25.
现在我们来使用这个类,将这个窗口绘制出来。
//========================
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine,
int cmdShow)
{
INITCOM;
MWindow w;
TestDirctWindow d3dwindow;
if (!d3dwindow.dx_Init(&
w)){
return 0;
}
w.SetTitle(“HellotWorld”);
w.Show();
return w.Run();
}
//========================
ok,我们这一讲的内容就这么简单的完成了吧,重点就是DirectX11库的初始化,这个初始化不要求是要理解,只要记住就好,把他放在基类的初始化就是因为这个原因,以后我们就不用在关心DirectX11的初始化啦,而是将更多的心思放在我们的任务上面。所以接下来我们可以在上面绘制一些简单的几何图形啦。
//============================================
回复D查看目录,回复数字查看相应章节
原文始发于微信公众号(
C/C++的编程教室
):第127讲 MDx11Frame(7)
|