概述

  • 本篇运用classD3DApp、vertex、inputLayout、vertexBuffer、indexBuffer、constantBuffer、pixelShader、vertexShader、CompileFromFile、rootSignature、descriptor table、MeshGeometry、psoStateObject绘制几何体

Direct3D渲染流程

创建windows窗口

Direct3D初始化

消息循环

渲染图形

应用程序结束,清除COM对象,程序退出

  • 最基础的D3DWinMain流程

header files:

#ifndef _boxheader_#include"d3dApp.h"#include"MathHelper.h"#include"UploadBuffer.h"using Microsoft::WRL::ComPtr;using namespace DirectX;using namespace DirectX::PackedVector;#endif//顶点属性struct Vertex{XMFLOAT3 Pos;XMFLOAT4 Color;};//常量缓冲区对象struct ObjectConstants{    //透视投影矩阵XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();};class BoxApp : public D3DApp{public:BoxApp(HINSTANCE hInstance);BoxApp(const BoxApp& rhs) = delete;BoxApp& operator=(const BoxApp& rhs) = delete;~BoxApp();    //D3D资源初始化virtual bool Initialize() override;private:    //若窗口大小发生改变,一些如深度模板缓冲区、后台缓冲区、视口等D3D属性需要改变virtual void OnResize() override;    //更新透视投影矩阵virtual void Update(const GameTimer& gt) override;    //渲染virtual void Draw(const GameTimer& gt) override;virtual void OnMouseDown(WPARAM btnState, int x, int y)  override;//鼠标被按下virtual void OnMouseUp(WPARAM btnState, int x, int y) override;//鼠标被松开virtual void OnMouseMove(WPARAM btnState, int x, int y) override;//鼠标移动void BuildDescriptorHeaps();//创建描述符堆void BuildConstantBuffers();//创建常量缓冲区void BuildRootSignature();//创建根签名void BuildPSO();//创建渲染流水线对象void BuildBoxGeometry();//创建几何辅助体,其中含有顶点缓冲区和索引缓冲区结构体void BuildShaderAndInputLayout();//创建着色器字节码和输入布局private:ComPtr mpRootSignature = nullptr;//根签名接口ComPtr mpCbvHeap = nullptr;//描述符堆接口std::unique_ptr<UploadBuffer> mppObjectCB = nullptr;//上传缓冲区里含常量对象std::unique_ptr mpBoxGeo = nullptr;//几何辅助体接口ComPtr mpvsByteBlob = nullptr;//vs字节码接口ComPtr mppsByteBlob = nullptr;//ps字节码接口std::vector mInputLayout;//输入布局数组ComPtr mpPSO = nullptr;//渲染流水线对象接口XMFLOAT4X4 mWorld = MathHelper::Identity4x4();//世界矩阵XMFLOAT4X4 mView = MathHelper::Identity4x4();//观测矩阵XMFLOAT4X4 mProj = MathHelper::Identity4x4();//透视投影矩阵float mTheta = 1.5f * XM_PI;//水平视场角float mPhi = XM_PIDIV4;    //垂直视场角float mRadius = 5.0f;//摄像机可视范围半径POINT mLastMousePos;//上一次鼠标的位置}; 

source files:WinMain()

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd){//启动调试层监听内存以及额外的调试功能#if defined(DEBUG) | defined(_DEBUG)_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);#endif    //生成BoxApp object,BoxApp进行初始化若成功则进入消息循环try{BoxApp theApp(hInstance);if (theApp.Initialize() == false){return 0;}return theApp.Run();}    //检测返回的HRESULT值,检测失败则抛出异常,显示调用出错的错误码、函数名、文件名、出错行号catch (DxException& e){MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);return 0;}}

Initialize()

bool BoxApp::Initialize(){    //先初始化D3D所需基本资源if (D3DApp::Initialize() == false){return false;}ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));    //创建Box所需资源BuildDescriptorHeaps();BuildConstantBuffers();BuildRootSignature();BuildShaderAndInputLayout();BuildBoxGeometry();BuildPSO();    //执行初始化命令    //因为命令加入命令列表后需Close(),引用命令列表需要将其重置,而重置前需要先将其Close()ThrowIfFailed(mCommandList->Close());ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);FlushCommandQueue();return true;}

Run()

  • 依然使用D3DApp基础的消息循环(Run)函数

OnResize()

void BoxApp::OnResize(){    //D3D基础OnResize函数D3DApp::OnResize();    //若调整窗口尺寸,则更新纵横比并重新计算透视投影矩阵XMMATRIX p = XMMatrixPerspectiveFovLH(0.25 * XM_PI, AspectRatio(), 1.0f, 1000.0f);XMStoreFloat4x4(&mProj, p);}

Update()

  • 位于消息循环
void BoxApp::Update(const GameTimer& gt){    //球坐标转换为笛卡尔坐标    //因为此处用orbiting camera system,摄像机涉及环绕物体旋转等操作,因此需要先用球坐标表示变换,再转换为笛卡尔坐标表示更方便float x = mRadius * sinf(mPhi) * cosf(mTheta);float y = mRadius * cosf(mPhi);float z = mRadius * sinf(mPhi) * sinf(mTheta);    //构建观测矩阵XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);XMVECTOR target = XMVectorZero();XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);XMMATRIX view = XMMatrixLookAtLH(pos, target, up);XMStoreFloat4x4(&mView, view);XMMATRIX world = XMLoadFloat4x4(&mWorld);XMMATRIX proj = XMLoadFloat4x4(&mProj);XMMATRIX worldViewProj = world * view * proj;    //更新常量缓冲区ObjectConstants objConstant;    //转置是因为存储在着色器内需要采用列式存储,采用右乘    //而在DirectXMath库中的矩阵数据在内存中采用行式存储,采用左乘XMStoreFloat4x4(&objConstant.WorldViewProj, XMMatrixTranspose(worldViewProj));mppObjectCB->CopyData(0, objConstant);}

Draw()

void BoxApp::Draw(const GameTimer& gt){    //复用记录命令所用的内存,只有GPU中命令列表执行完后才对其重置ThrowIfFailed(mDirectCmdListAlloc->Reset());    //将命令列表加入命令队列后对其进行重置ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), mpPSO.Get()));    //设置viewport和scissor rectanglemCommandList->RSSetViewports(1, &mScreenViewport);mCommandList->RSSetScissorRects(1, &mScissorRect);    //资源转换:将呈现状态转换为渲染目标状态mCommandList->ResourceBarrier(1,&CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));    //将渲染目标设定为指定颜色mCommandList->ClearRenderTargetView(CurrentBackBufferView(),Colors::LightSteelBlue,0,nullptr);    //清理深度模板缓冲区mCommandList->ClearDepthStencilView(DepthStencilView(),D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,1.0f,0,0,nullptr);    //设置渲染流水线的render target缓冲区和depth stencil缓冲区mCommandList->OMSetRenderTargets(1,&CurrentBackBufferView(),true,&DepthStencilView());ID3D12DescriptorHeap* descriptorHeaps[] = { mpCbvHeap.Get() };mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);mCommandList->SetGraphicsRootSignature(mpRootSignature.Get());mCommandList->SetGraphicsRootDescriptorTable(0, mpCbvHeap->GetGPUDescriptorHandleForHeapStart());mCommandList->IASetVertexBuffers(0, 1, &mpBoxGeo->VertexBufferView());mCommandList->IASetIndexBuffer(&mpBoxGeo->IndexBufferView());mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);    //绘制顶点mCommandList->DrawIndexedInstanced(mpBoxGeo->DrawArgs["box"].IndexCount,1, 0, 0, 0);    //资源转换:渲染目标到呈现mCommandList->ResourceBarrier(1,&CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),D3D12_RESOURCE_STATE_RENDER_TARGET,D3D12_RESOURCE_STATE_PRESENT));ThrowIfFailed(mCommandList->Close());ID3D12CommandList* cmdLists[] = { mCommandList.Get() };mCommandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);    //交换前后台缓冲区ThrowIfFailed(mSwapChain->Present(0, 0));mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;FlushCommandQueue();}

OnMouseDown()

  • 按下鼠标时
void BoxApp::OnMouseDown(WPARAM btnState, int x, int y){    //上次鼠标位置mLastMousePos.x = x;mLastMousePos.y = y;SetCapture(mhMainWnd);//将鼠标捕获设置为属于当前线程的指定窗口}

OnMouseUp()

  • 释放当前线程中一个窗口的鼠标捕获,并恢复正常的鼠标输入处理
void BoxApp::OnMouseUp(WPARAM btnState, int x, int y){ReleaseCapture();//从当前线程中的窗口释放鼠标捕获,并恢复正常的鼠标输入处理}

OnMouseMove()

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y){if ((btnState & MK_LBUTTON) != 0){//根据鼠标移动距离计算旋转角度并令每个像素都以此角度的1/4旋转float dx = XMConvertToRadians(0.25f * static_cast(x - mLastMousePos.x));float dy = XMConvertToRadians(0.25f * static_cast(y - mLastMousePos.y));//以鼠标输入更新摄像机绕立方体旋转的角度mTheta += dx;mPhi += dy;//限制垂直视场角范围mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);//Restricts a value to be within a specified range}else if((btnState & MK_RBUTTON) != 0 ){//每个像素按鼠标移动距离的0.005倍缩放float dx = 0.005f * static_cast(x - mLastMousePos.x);float dy = 0.005f * static_cast(y - mLastMousePos.y);//以鼠标输入更新摄像机的可视范围半径mRadius += dx - dy;//限制可视半径范围mRadius = MathHelper::Clamp(mRadius, 3.0f, 15.0f);}mLastMousePos.x = x;mLastMousePos.y = y;}

BuildDescriptorHeaps()

void BoxApp::BuildDescriptorHeaps(){D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;cbvHeapDesc.NumDescriptors = 1;cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;cbvHeapDesc.NodeMask = 0;ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,IID_PPV_ARGS(&mpCbvHeap)))}

BuildConstantBuffers()

  • 利用d3dUtil.h内的UploadBuffer类完成上传缓冲区的相关操作
void BoxApp::BuildConstantBuffers(){    //利用UploadBuffer构造函数完成计算mElementByteSize、createCommittedResource()、map()mppObjectCB = std::make_unique<UploadBuffer>(md3dDevice.Get(), 1, true);UINT mppObjectCBSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));    //Resource()获得UploadBuffer地址,GetGPUVirtualAddress获得缓冲区起始地址D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mppObjectCB->Resource()->GetGPUVirtualAddress();        //偏移到常量缓冲区内第i个物体int boxCBufferIndex = 0;cbAddress += boxCBufferIndex * mppObjectCBSize;D3D12_CONSTANT_BUFFER_VIEW_DESC cbvViewDesc;cbvViewDesc.BufferLocation = cbAddress;cbvViewDesc.SizeInBytes = mppObjectCBSize;md3dDevice->CreateConstantBufferView(&cbvViewDesc,mpCbvHeap->GetCPUDescriptorHandleForHeapStart());}

BuildRootSignature()

  • 此处只涉及描述符表
void BoxApp::BuildRootSignature(){    //定义根参数CD3DX12_ROOT_PARAMETER slotRootParameter[1];    //定义初始化描述符表并初始化根参数CD3DX12_DESCRIPTOR_RANGE cbvTable;cbvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV,1,0);slotRootParameter[0].InitAsDescriptorTable(1,&cbvTable);    //定义根签名描述符CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1,slotRootParameter,0,nullptr,D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);    //根签名的描述布局必须先进行序列化处理(将对象的状态信息转换为可以存储或传输的形式的过程),    //其转换为以ID3DBlob接口表示的序列化数据格式后,才可将其传入CreateRootSignature方法,正式创建根签名ComPtrserializedRootSignature = nullptr;ComPtrerrorBlob = nullptr;auto hr = D3D12SerializeRootSignature(&rootSigDesc,D3D_ROOT_SIGNATURE_VERSION_1,serializedRootSignature.GetAddressOf(),errorBlob.GetAddressOf());if (errorBlob != nullptr){::OutputDebugStringA((char*)errorBlob->GetBufferPointer());}ThrowIfFailed(hr);ThrowIfFailed(md3dDevice->CreateRootSignature(0,serializedRootSignature->GetBufferPointer(),serializedRootSignature->GetBufferSize(),IID_PPV_ARGS(&mpRootSignature)));}

BuildShaderAndInputLayout()

void BoxApp::BuildShaderAndInputLayout(){HRESULT hr = S_OK;    //将着色器程序编译为可移植的字节码给图形驱动程序,驱动程序则会将其重新编译为当前GPU优化的本地指令[ATI1]mpvsByteBlob = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "VS", "vs_5_0");mppsByteBlob = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "PS", "ps_5_0");    ////D3D12_INPUT_ELEMENT_DESC数组描述顶点结构体对应成员mInputLayout ={{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};}

BuildBoxGeometry()

void BoxApp::BuildBoxGeometry(){    //顶点std::array vertices ={//后Vertex{XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(Colors::White)},//左上Vertex{XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(Colors::Black)},//左下Vertex{XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(Colors::Black)},//右上Vertex{XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(Colors::White)},//右下//前Vertex{XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(Colors::Black)},//左上Vertex{XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White)},//左下Vertex{XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(Colors::White)},//右上Vertex{XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Black)}//右下};    //按此索引顺序绘制std::array indices ={//前5, 4, 6,5, 6, 7,//后1, 0, 2,1, 2, 3,//左1, 0, 4,1, 4, 5,//右3, 2, 6,3, 6, 7,//上4, 0, 2,4, 2, 6,//下5, 1, 3,5, 3, 7};const UINT VBSize = (UINT)vertices.size() * sizeof(Vertex);const UINT IBSize = (UINT)indices.size() * sizeof(std::uint16_t);mpBoxGeo = std::make_unique();    //指定此几何体网格集合的名称,如此即可根据此名找到tampBoxGeo->Name = "boxGeo";ThrowIfFailed(D3DCreateBlob(VBSize, &mpBoxGeo->VertexBufferCPU));CopyMemory(mpBoxGeo->VertexBufferCPU->GetBufferPointer(), &vertices, VBSize);ThrowIfFailed(D3DCreateBlob(IBSize, &mpBoxGeo->IndexBufferCPU));CopyMemory(mpBoxGeo->IndexBufferCPU->GetBufferPointer(), &indices, IBSize);mpBoxGeo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),mCommandList.Get(),vertices.data(),VBSize,mpBoxGeo->VertexBufferUploader);mpBoxGeo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),mCommandList.Get(),indices.data(),IBSize,mpBoxGeo->IndexBufferUploader);    //缓冲区相关数据mpBoxGeo->VertexByteStride = sizeof(Vertex);mpBoxGeo->VertexBufferByteSize = sizeof(vertices);mpBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT;mpBoxGeo->IndexBufferByteSize = sizeof(indices);SubmeshGeometry submesh{(UINT)indices.size(),0,0};mpBoxGeo->DrawArgs["box"] = submesh;}

BuildPSO()

void BoxApp::BuildPSO(){D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;    //用0填充此内存区域,不让结构的成员数值具有不确定性ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));psoDesc.pRootSignature = mpRootSignature.Get();psoDesc.VS ={reinterpret_cast(mpvsByteBlob->GetBufferPointer()),mpvsByteBlob->GetBufferSize()};psoDesc.PS ={reinterpret_cast(mppsByteBlob->GetBufferPointer()),mppsByteBlob->GetBufferSize()};psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);psoDesc.SampleMask = UINT_MAX;psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);psoDesc.InputLayout ={mInputLayout.data(),(UINT)mInputLayout.size()};psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;psoDesc.NumRenderTargets = 1;psoDesc.RTVFormats[0] = mBackBufferFormat;psoDesc.DSVFormat = mDepthStencilFormat;psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;psoDesc.NodeMask = 0;ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mpPSO)));}