概述
- 本篇运用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 onm ouseDown(WPARAM btnState, int x, int y) override; //鼠标被按下
virtual void onm ouseUp(WPARAM btnState, int x, int y) override; //鼠标被松开
virtual void onm ouseMove(WPARAM btnState, int x, int y) override; //鼠标移动
void BuildDescriptorHeaps(); //创建描述符堆
void BuildConstantBuffers(); //创建常量缓冲区
void BuildRootSignature(); //创建根签名
void BuildPSO(); //创建渲染流水线对象
void BuildBoxGeometry(); //创建几何辅助体,其中含有顶点缓冲区和索引缓冲区结构体
void BuildShaderAndInputLayout(); //创建着色器字节码和输入布局
private:
ComPtr<ID3D12RootSignature> mpRootSignature = nullptr; //根签名接口
ComPtr<ID3D12DescriptorHeap> mpCbvHeap = nullptr; //描述符堆接口
std::unique_ptr<UploadBuffer<ObjectConstants>> mppObjectCB = nullptr; //上传缓冲区里含常量对象
std::unique_ptr<MeshGeometry> mpBoxGeo = nullptr; //几何辅助体接口
ComPtr<ID3DBlob> mpvsByteBlob = nullptr; //vs字节码接口
ComPtr<ID3DBlob> mppsByteBlob = nullptr; //ps字节码接口
std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout; //输入布局数组
ComPtr<ID3D12PipelineState> 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 rectangle
mCommandList->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<float>(x - mLastMousePos.x));
float dy = XMConvertToRadians(0.25f * static_cast<float>(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<float>(x - mLastMousePos.x);
float dy = 0.005f * static_cast<float>(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<ObjectConstants>>(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方法,正式创建根签名
ComPtr<ID3DBlob>serializedRootSignature = nullptr;
ComPtr<ID3DBlob>errorBlob = 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<Vertex, 8> 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<std::uint16_t, 36> 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<MeshGeometry>();
//指定此几何体网格集合的名称,如此即可根据此名找到ta
mpBoxGeo->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<BYTE*>(mpvsByteBlob->GetBufferPointer()),
mpvsByteBlob->GetBufferSize()
};
psoDesc.PS =
{
reinterpret_cast<BYTE*>(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)));
}
标签:1.0,实现,void,BoxApp,几何体,mCommandList,mpBoxGeo,D3D12,绘制
From: https://www.cnblogs.com/chenglixue/p/16754641.html