第127讲 MDx11Frame(7)

上一讲我们主要介绍了一个基类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();

}


//========================

第127讲 MDx11Frame(7)


ok,我们这一讲的内容就这么简单的完成了吧,重点就是DirectX11库的初始化,这个初始化不要求是要理解,只要记住就好,把他放在基类的初始化就是因为这个原因,以后我们就不用在关心DirectX11的初始化啦,而是将更多的心思放在我们的任务上面。所以接下来我们可以在上面绘制一些简单的几何图形啦。


//============================================

回复D查看目录,回复数字查看相应章节


原文始发于微信公众号(

C/C++的编程教室

):第127讲 MDx11Frame(7)

|

发表评论