- Published on
DX学习笔记(五):绘制几何体2
- Authors

- Name
- 东哥
帧资源
上一篇在绘制每一帧都会进行一次CPU与GPU的同步,目的是
- GPU未结束命令分配器中所有命令执行之前,不能重置,如果重置,那么GPU当前还未处理的命令就会被清除
- 对于常量缓冲区的数据而言,不同步的话会导致数据异常
所以我们用FlushCommandQueue来保持同步
但是有2个缺点导致性能浪费
- 每一帧起始阶段GPU无任务,处于闲置状态
- 帧结尾CPU处于闲置状态等待GPU完成绘制
帧资源
CPU会比GPU提前两帧工作,以保持GPU可持续工作
//存有CPU为构建美珍命令列表所需的资源
//其中的数据将依程序而异,取决于实际绘制所需的资源
struct FrameResource
{
public:
FrameResource(ID3D12Device* device, UINT passCount, UINT objectCount);
FrameResource(const FrameResource& rhs) = delete;
FrameResource& operator=(const FrameResource& rhs) = delete;
~FrameResource();
// CPU未结束命令分配器中所有命令执行之前,不能重置
//因此每一帧都要有他们自己的命令分配器
Microsoft::WRL::`ComPtr<ID3D12CommandAllocator>` CmdListAlloc;
//在GPU完成引用此常量缓冲区的命令之前,我们不能对他进行更新
//因此每一帧都要有他们自己的常量缓冲区
std::unique_ptr<`UploadBuffer<PassConstants>`> PassCB = nullptr;
std::unique_ptr<`UploadBuffer<ObjectConstants>`> ObjectCB = nullptr;
// 通过围栏值将命令标记到此围栏点,这是我们可以检测到GPU是否还在使用这些帧资源
UINT64 Fence = 0;
};
FrameResource::FrameResource(ID3D12Device* device, UINT passCount, UINT objectCount)
{
ThrowIfFailed(device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(CmdListAlloc.GetAddressOf())));
PassCB = std::make_unique<`UploadBuffer<PassConstants>`>(device, passCount, true);
ObjectCB = std::make_unique<`UploadBuffer<ObjectConstants>`>(device, objectCount, true);
}
然后我们的应用程序类将用一个vector容器实例化3个帧资源对象
const int gNumFrameResources = 3;
std::vector<std::unique_ptr<FrameResource>> mFrameResources;
FrameResource* mCurrFrameResource = nullptr;
int mCurrFrameResourceIndex = 0;
void ShapesApp::BuildFrameResources()
{
for(int i = 0; i < gNumFrameResources; ++i)
{
mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
1, (UINT)mAllRitems.size()));
}
}
然后在CPU处理第n帧的算法是如下
void ShapesApp::Update(const GameTimer& gt)
{
// 循环获取帧资源数组中的元素
mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
//GPU是否执行完所有命令?
//如果还没有就令CPU等待,直到GPU完成所有命令的执行并且抵达这个围栏点
if(mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
}
void ShapesApp::Draw(const GameTimer& gt)
{
//.....构建命令列表
//增加围栏点,将命令标记到此围栏点
mCurrFrameResource->Fence = ++mCurrentFence;
//向命令列表添加一条指令来设置一个新的围栏点
//由于当前的GPU正在执行绘制命令,所以在GPU处理完Signal()函数之前的所有命令以前,并不会设置新的围栏点
mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
这种处理无法完全避免等待的发生
如果GPU速度远大于CPU,,则GPU会处于空闲
反之,CPU会空闲,但是CPU空闲是有利的,因为CPU还需要处理其他算法
采用多帧资源的方法目的是让命令队列一直处于非空状态,即GPU一直有事情可以做
渲染项
单次绘制调用过程中,需要向渲染流水线提交的数据集成为渲染项
我们用一个轻量级的结构体来存储绘制物体所需的数据
struct RenderItem
{
RenderItem() = default;
// 描述了局部空间相对于世界空间的矩阵
//定义了物体位于世界空间的位置,朝向和大小
XMFLOAT4X4 World = MathHelper::Identity4x4();
// 用dirty flag来标志物体的相关数据已经发生改变,意味着我们次数需要更新常量缓冲区
// 由于每个帧资源中都有一个物体常量缓冲区,所以需要对每个帧资源进行更新
//即,当修改物体数据时,应当按NumFramesDirty= gNumFrameReousreces进行设置,从而使每个帧资源都更新
int NumFramesDirty = gNumFrameResources;
// 该索引指向的GPU常量缓冲区对应当前渲染项中的物体常量缓冲区
UINT ObjCBIndex = -1;
MeshGeometry* Geo = nullptr;
//图元拓扑
D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
// DrawIndexedInstanced方法的参数
UINT IndexCount = 0;
UINT StartIndexLocation = 0;
int BaseVertexLocation = 0;
};
渲染过程中所用到的常量数据
- 扩展常量缓冲区
//着色器
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
};
//与之对应的常量缓冲区数据结构
struct PassConstants
{
DirectX::XMFLOAT4X4 View = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvView = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 Proj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvProj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 ViewProj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvViewProj = MathHelper::Identity4x4();
DirectX::XMFLOAT3 EyePosW = { 0.0f, 0.0f, 0.0f };
float cbPerObjectPad1 = 0.0f;
DirectX::XMFLOAT2 RenderTargetSize = { 0.0f, 0.0f };
DirectX::XMFLOAT2 InvRenderTargetSize = { 0.0f, 0.0f };
float NearZ = 0.0f;
float FarZ = 0.0f;
float TotalTime = 0.0f;
float DeltaTime = 0.0f;
};
- 刷新常量缓冲区
void ShapesApp::UpdateObjectCBs(const GameTimer& gt)
{
auto currObjectCB = mCurrFrameResource->ObjectCB.get();
for(auto& e : mAllRitems)
{
// 只对发生常量改变的缓冲区刷新
if(e->NumFramesDirty > 0)
{
XMMATRIX world = XMLoadFloat4x4(&e->World);
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
currObjectCB->CopyData(e->ObjCBIndex, objConstants);
// 还需要对下一个FrameResource更新
e->NumFramesDirty--;
}
}
}
void ShapesApp::UpdateMainPassCB(const GameTimer& gt)
{
XMMATRIX view = XMLoadFloat4x4(&mView);
XMMATRIX proj = XMLoadFloat4x4(&mProj);
XMMATRIX viewProj = XMMatrixMultiply(view, proj);
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
mMainPassCB.EyePosW = mEyePos;
mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
mMainPassCB.NearZ = 1.0f;
mMainPassCB.FarZ = 1000.0f;
mMainPassCB.TotalTime = gt.TotalTime();
mMainPassCB.DeltaTime = gt.DeltaTime();
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(0, mMainPassCB);
}
- 着色器改变
VertexOut VS(VertexIn vin)
{
VertexOut vout;
// Transform to homogeneous clip space.
float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
vout.PosH = mul(posW, gViewProj);
// Just pass vertex color into the pixel shader.
vout.Color = vin.Color;
return vout;
}
- 调整根签名
void ShapesApp::BuildRootSignature()
{
// 创建2个描述
CD3DX12_DESCRIPTOR_RANGE cbvTable0;
cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
CD3DX12_DESCRIPTOR_RANGE cbvTable1;
cbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);
// 2个成员的描述符表
CD3DX12_ROOT_PARAMETER slotRootParameter[2];
// 创建根CBV
slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
slotRootParameter[1].InitAsDescriptorTable(1, &cbvTable1);
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
//*************************
}
不同形状的几何体
GeometryGenerator类
class GeometryGenerator
{
public:
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
struct Vertex
{
Vertex(){}
Vertex(
const DirectX::XMFLOAT3& p,
const DirectX::XMFLOAT3& n,
const DirectX::XMFLOAT3& t,
const DirectX::XMFLOAT2& uv) :
Position(p),
Normal(n),
TangentU(t),
TexC(uv){}
Vertex(
float px, float py, float pz,
float nx, float ny, float nz,
float tx, float ty, float tz,
float u, float v) :
Position(px,py,pz),
Normal(nx,ny,nz),
TangentU(tx, ty, tz),
TexC(u,v){}
DirectX::XMFLOAT3 Position;
DirectX::XMFLOAT3 Normal;
DirectX::XMFLOAT3 TangentU;
DirectX::XMFLOAT2 TexC;
};
struct MeshData
{
std::vector<Vertex> Vertices;
std::vector<uint32> Indices32;
std::vector<uint16>& GetIndices16()
{
if(mIndices16.empty())
{
mIndices16.resize(Indices32.size());
for(size_t i = 0; i < Indices32.size(); ++i)
mIndices16[i] = static_cast<uint16>(Indices32[i]);
}
return mIndices16;
}
private:
std::vector<uint16> mIndices16;
};
//创建盒子
MeshData CreateBox(float width, float height, float depth, uint32 numSubdivisions);
//创建球体
MeshData CreateSphere(float radius, uint32 sliceCount, uint32 stackCount);
//创建以给定的半径创建一个以原点为中心的地层
MeshData CreateGeosphere(float radius, uint32 numSubdivisions);
///创建一个平行于y轴并以原点为中心的圆柱体。
///上下半径可以变化以形成各种圆锥形状,而不是真实的圆柱体。 切片和堆栈参数控制镶嵌的程度。
MeshData CreateCylinder(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount);
//在xz平面中以m行和n列为中心创建一个mxn网格,在具有指定宽度和深度的原点。
MeshData CreateGrid(float width, float depth, uint32 m, uint32 n);
//创建与屏幕对齐的四边形。 这对于后期处理和屏幕效果很有用。
MeshData CreateQuad(float x, float y, float w, float h, float depth);
private:
void Subdivide(MeshData& meshData);
Vertex MidPoint(const Vertex& v0, const Vertex& v1);
void BuildCylinderTopCap(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount, MeshData& meshData);
void BuildCylinderBottomCap(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount, MeshData& meshData);
};
细分
void GeometryGenerator::Subdivide(MeshData& meshData)
{
// v1
// *
// / \
// / \
// m0*-----*m1
// / \ / \
// / \ / \
// *-----*-----*
// v0 m2 v2
//****我里个去*****
//保存一份拷贝
MeshData inputCopy=meshData;
meshData.Vertices.resize(0);
meshData.Indices32.reserve(0);//清空
uint32 numTris =(uint32)inputCopy.Indices32.size()/3; //三角形数量
for (uint32 i=0;i<numTris;++i)
{
Vertex v0 = inputCopy.Vertices[inputCopy.Indices32[i * 3 + 0]];
Vertex v1 = inputCopy.Vertices[inputCopy.Indices32[i * 3 + 1]];
Vertex v2 = inputCopy.Vertices[inputCopy.Indices32[i * 3 + 2]];
//创建中间点
Vertex m0 = MidPoint(v0, v1);
Vertex m1 = MidPoint(v1, v2);
Vertex m2 = MidPoint(v0, v2);
//添加新的几何体
meshData.Vertices.push_back(v0); // 0
meshData.Vertices.push_back(v1); // 1
meshData.Vertices.push_back(v2); // 2
meshData.Vertices.push_back(m0); // 3
meshData.Vertices.push_back(m1); // 4
meshData.Vertices.push_back(m2); // 5
meshData.Indices32.push_back(i * 6 + 0);
meshData.Indices32.push_back(i * 6 + 3);
meshData.Indices32.push_back(i * 6 + 5);
meshData.Indices32.push_back(i * 6 + 3);
meshData.Indices32.push_back(i * 6 + 4);
meshData.Indices32.push_back(i * 6 + 5);
meshData.Indices32.push_back(i * 6 + 5);
meshData.Indices32.push_back(i * 6 + 4);
meshData.Indices32.push_back(i * 6 + 2);
meshData.Indices32.push_back(i * 6 + 3);
meshData.Indices32.push_back(i * 6 + 1);
meshData.Indices32.push_back(i * 6 + 4);
}
}
中间点的生成
Vertex GeometryGenerator::MidPoint(const Vertex& v0, const Vertex& v1)
{
XMVECTOR p0 = XMLoadFloat3(&v0.Position);
XMVECTOR p1 = XMLoadFloat3(&v1.Position);
XMVECTOR n0 = XMLoadFloat3(&v0.Normal);
XMVECTOR n1 = XMLoadFloat3(&v1.Normal);
XMVECTOR tan0 = XMLoadFloat3(&v0.TangentU);
XMVECTOR tan1 = XMLoadFloat3(&v1.TangentU);
XMVECTOR tex0 = XMLoadFloat2(&v0.TexC);
XMVECTOR tex1 = XMLoadFloat2(&v1.TexC);
//计算中间数据
XMVECTOR pos=0.5f*(p0+p1);
XMVECTOR normal=XMVector3Normalize(0.5f*(n0+n1));
XMVECTOR tangent = XMVector3Normalize(0.5f*(tan0 + tan1));
XMVECTOR tex = 0.5f*(tex0 + tex1);
Vertex v;
XMStoreFloat3(&v.Position,pos);
XMStoreFloat3(&v.Normal, normal);
XMStoreFloat3(&v.TangentU, tangent);
XMStoreFloat2(&v.TexC, tex);
return v;
}
盒子
GeometryGenerator::MeshData GeometryGenerator::CreateBox(float width, float height, float depth, uint32 numSubdivisions)
{
MeshData meshData;
/*
构建顶点
*/
Vertex v[24];
float w2 = 0.5f*width;
float h2 = 0.5f*height;
float d2 = 0.5f*depth;
// 前面
v[0] = Vertex(-w2, -h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[1] = Vertex(-w2, +h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[2] = Vertex(+w2, +h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
v[3] = Vertex(+w2, -h2, -d2, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
// 后面
v[4] = Vertex(-w2, -h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[5] = Vertex(+w2, -h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[6] = Vertex(+w2, +h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[7] = Vertex(-w2, +h2, +d2, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
// 顶部面
v[8] = Vertex(-w2, +h2, -d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[9] = Vertex(-w2, +h2, +d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[10] = Vertex(+w2, +h2, +d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
v[11] = Vertex(+w2, +h2, -d2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
// 底部面
v[12] = Vertex(-w2, -h2, -d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
v[13] = Vertex(+w2, -h2, -d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
v[14] = Vertex(+w2, -h2, +d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
v[15] = Vertex(-w2, -h2, +d2, 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
// 左边面
v[16] = Vertex(-w2, -h2, +d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[17] = Vertex(-w2, +h2, +d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[18] = Vertex(-w2, +h2, -d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[19] = Vertex(-w2, -h2, -d2, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
// 右边面
v[20] = Vertex(+w2, -h2, -d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
v[21] = Vertex(+w2, +h2, -d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
v[22] = Vertex(+w2, +h2, +d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
v[23] = Vertex(+w2, -h2, +d2, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
meshData.Vertices.assign(&v[0],&v[24]);
/*
构建序列
*/
uint32 i[36];
// Fill in the front face index data
i[0] = 0; i[1] = 1; i[2] = 2;
i[3] = 0; i[4] = 2; i[5] = 3;
// Fill in the back face index data
i[6] = 4; i[7] = 5; i[8] = 6;
i[9] = 4; i[10] = 6; i[11] = 7;
// Fill in the top face index data
i[12] = 8; i[13] = 9; i[14] = 10;
i[15] = 8; i[16] = 10; i[17] = 11;
// Fill in the bottom face index data
i[18] = 12; i[19] = 13; i[20] = 14;
i[21] = 12; i[22] = 14; i[23] = 15;
// Fill in the left face index data
i[24] = 16; i[25] = 17; i[26] = 18;
i[27] = 16; i[28] = 18; i[29] = 19;
// Fill in the right face index data
i[30] = 20; i[31] = 21; i[32] = 22;
i[33] = 20; i[34] = 22; i[35] = 23;
meshData.Indices32.assign(&i[0],&i[36]);
//细分
numSubdivisions = std::min<uint32>(numSubdivisions,6u);
for (uint32 i = 0;i<numSubdivisions;++i)
{
Subdivide(meshData);//细分
}
return meshData;
}
球体
GeometryGenerator::MeshData GeometryGenerator::CreateSphere(float radius, uint32 sliceCount, uint32 stackCount)
{
MeshData meshData;
Vertex topVertex(0.0f,+radius, 0.0f, 0.0f, +1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);//顶部顶点
Vertex bottomVertex(0.0f, -radius, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);//底部顶点
meshData.Vertices.push_back(topVertex);//添加顶部顶点
float phiStep = XM_PI/stackCount;//水平面分段
float thetaStep=2.0f*XM_PI/sliceCount;//竖直面分段
for (uint32 i=0;i<=stackCount-1;++i)
{
float phi = i*phiStep;//yaw角度
for (uint32 j = 0 ;j<=sliceCount;++j)
{
float theta=j*thetaStep;//pitch角度
Vertex v;
v.Position.x = radius*sinf(phi)*cosf(theta);
v.Position.y = radius * cosf(phi);
v.Position.z = radius * sinf(phi)*sinf(theta);
v.TangentU.x = -radius * sinf(phi)*sinf(theta);
v.TangentU.y = 0.0f;
v.TangentU.z = +radius * sinf(phi)*cosf(theta);
XMVECTOR T = XMLoadFloat3(&v.TangentU);
XMStoreFloat3(&v.TangentU, XMVector3Normalize(T));
XMVECTOR p = XMLoadFloat3(&v.Position);
XMStoreFloat3(&v.Normal, XMVector3Normalize(p));
v.TexC.x = theta / XM_2PI;
v.TexC.y = phi / XM_PI;
meshData.Vertices.push_back(v);
}
}
//最底下一个顶点
meshData.Vertices.push_back(bottomVertex);
//顶部面所有点的索引
for (uint32 i = 1; i <= sliceCount; ++i)
{
meshData.Indices32.push_back(0);
meshData.Indices32.push_back(i + 1);
meshData.Indices32.push_back(i);
}
uint32 baseIndex = 1;
uint32 ringVertexCount = sliceCount + 1;
//高度细分数量-2,因为顶部底部不算在内
for (uint32 i = 0; i < stackCount - 2; ++i)
{
for (uint32 j = 0; j < sliceCount; ++j)
{
meshData.Indices32.push_back(baseIndex + i * ringVertexCount + j);
meshData.Indices32.push_back(baseIndex + i * ringVertexCount + j + 1);
meshData.Indices32.push_back(baseIndex + (i + 1)*ringVertexCount + j);
meshData.Indices32.push_back(baseIndex + (i + 1)*ringVertexCount + j);
meshData.Indices32.push_back(baseIndex + i * ringVertexCount + j + 1);
meshData.Indices32.push_back(baseIndex + (i + 1)*ringVertexCount + j + 1);
}
}
//底部的点的序号
uint32 southPoleIndex = (uint32)meshData.Vertices.size() - 1;
baseIndex = southPoleIndex - ringVertexCount;
//底部面所有索引
for (uint32 i = 0; i < sliceCount; ++i)
{
meshData.Indices32.push_back(southPoleIndex);
meshData.Indices32.push_back(baseIndex + i);
meshData.Indices32.push_back(baseIndex + i + 1);
}
return meshData;
}
柱体网格

GeometryGenerator::MeshData GeometryGenerator::CreateCylinder(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount)
{
MeshData meshData;
//高度递增
float stackHeight = height / stackCount;
//从底部开始,每一次半径递增
float radiusStep = (topRadius - bottomRadius) / stackCount;
//环数
uint32 rightCount = stackCount +1;
for (uint32 i=0;i<rightCount;++i)
{
float y = -0.5f*height + i*stackHeight;//从底部开始往上加高度
float r = bottomRadius+i*radiusStep;
float dTheta= 2.0f*XM_PI/sliceCount;//360角度范围的弧度,水平角度yaw
for (uint32 j=0;j<=sliceCount;++j)
{
Vertex vertex;
float c = cosf(j*dTheta);
float s = sinf(j*dTheta);
vertex.Position=XMFLOAT3(r*c,y,r*s);
vertex.TexC.x = (float)j/sliceCount;
vertex.TexC.y = 1.0f - (float)i / stackCount;
vertex.TangentU = XMFLOAT3(-s, 0.0f, c);
float dr = bottomRadius - topRadius;
XMFLOAT3 bitangent(dr*c, -height, dr*s);
XMVECTOR T = XMLoadFloat3(&vertex.TangentU);
XMVECTOR B = XMLoadFloat3(&bitangent);
XMVECTOR N = XMVector3Normalize(XMVector3Cross(T, B));
XMStoreFloat3(&vertex.Normal, N);
meshData.Vertices.push_back(vertex);
}
}
uint32 ringVertexCount = sliceCount + 1;
for (uint32 i = 0; i < stackCount; ++i)
{
for (uint32 j = 0; j < sliceCount; ++j)
{
meshData.Indices32.push_back(i*ringVertexCount + j);
meshData.Indices32.push_back((i + 1)*ringVertexCount + j);
meshData.Indices32.push_back((i + 1)*ringVertexCount + j + 1);
meshData.Indices32.push_back(i*ringVertexCount + j);
meshData.Indices32.push_back((i + 1)*ringVertexCount + j + 1);
meshData.Indices32.push_back(i*ringVertexCount + j + 1);
}
}
BuildCylinderTopCap(bottomRadius, topRadius, height, sliceCount, stackCount, meshData);
BuildCylinderBottomCap(bottomRadius, topRadius, height, sliceCount, stackCount, meshData);
return meshData;
}
每一个环最后一个顶点与第一个顶点是重合的,但是纹理坐标不同,只有这样才能保证绘制正确纹理
构建顶部和底部
void GeometryGenerator::BuildCylinderTopCap(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount, MeshData& meshData)
{
uint32 baseIndex = (uint32)meshData.Vertices.size();
float y = 0.5f*height;
float dTheta = 2.0f*XM_PI / sliceCount;
//顶点重复,因为纹理坐标和法线不同。
for (uint32 i = 0; i <= sliceCount; ++i)
{
float x = topRadius * cosf(i*dTheta);
float z = topRadius * sinf(i*dTheta);
//按高度缩小以尝试使顶盖纹理坐标区域与底面成比例??
float u = x / height + 0.5f;
float v = z / height + 0.5f;
meshData.Vertices.push_back(Vertex(x, y, z, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, u, v));
}
// 中心点
meshData.Vertices.push_back(Vertex(0.0f, y, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f));
// 中心序号
uint32 centerIndex = (uint32)meshData.Vertices.size() - 1;
for (uint32 i = 0; i < sliceCount; ++i)
{
meshData.Indices32.push_back(centerIndex);
meshData.Indices32.push_back(baseIndex + i + 1);
meshData.Indices32.push_back(baseIndex + i);
}
}
void GeometryGenerator::BuildCylinderBottomCap(float bottomRadius, float topRadius, float height, uint32 sliceCount, uint32 stackCount, MeshData& meshData)
{
uint32 baseIndex = (uint32)meshData.Vertices.size();
float y = -0.5f*height;
float dTheta = 2.0f*XM_PI / sliceCount;
for (uint32 i = 0; i <= sliceCount; ++i)
{
float x = bottomRadius * cosf(i*dTheta);
float z = bottomRadius * sinf(i*dTheta);
float u = x / height + 0.5f;
float v = z / height + 0.5f;
meshData.Vertices.push_back(Vertex(x, y, z, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, u, v));
}
meshData.Vertices.push_back(Vertex(0.0f, y, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f));
uint32 centerIndex = (uint32)meshData.Vertices.size() - 1;
for (uint32 i = 0; i < sliceCount; ++i)
{
meshData.Indices32.push_back(centerIndex);
meshData.Indices32.push_back(baseIndex + i);
meshData.Indices32.push_back(baseIndex + i + 1);
}
}
几何球体
GeometryGenerator::MeshData GeometryGenerator::CreateGeosphere(float radius, uint32 numSubdivisions)
{
MeshData meshData;
// 为细分数设置上限。
numSubdivisions = std::min<uint32>(numSubdivisions, 6u);
// 通过镶嵌二十面体来近似一个球体。
const float X = 0.525731f;
const float Z = 0.850651f;
XMFLOAT3 pos[12] =
{
XMFLOAT3(-X, 0.0f, Z), XMFLOAT3(X, 0.0f, Z),
XMFLOAT3(-X, 0.0f, -Z), XMFLOAT3(X, 0.0f, -Z),
XMFLOAT3(0.0f, Z, X), XMFLOAT3(0.0f, Z, -X),
XMFLOAT3(0.0f, -Z, X), XMFLOAT3(0.0f, -Z, -X),
XMFLOAT3(Z, X, 0.0f), XMFLOAT3(-Z, X, 0.0f),
XMFLOAT3(Z, -X, 0.0f), XMFLOAT3(-Z, -X, 0.0f)
};
uint32 k[60] =
{
1,4,0, 4,9,0, 4,5,9, 8,5,4, 1,8,4,
1,10,8, 10,3,8, 8,3,5, 3,2,5, 3,7,2,
3,10,7, 10,6,7, 6,11,7, 6,0,11, 6,1,0,
10,1,6, 11,0,9, 2,11,9, 5,2,9, 11,2,7
};
meshData.Vertices.resize(12);
meshData.Indices32.assign(&k[0], &k[60]);
for(uint32 i = 0; i < 12; ++i)
meshData.Vertices[i].Position = pos[i];
for(uint32 i = 0; i < numSubdivisions; ++i)
Subdivide(meshData);
//将顶点投影到球体和比例上。
for(uint32 i = 0; i < meshData.Vertices.size(); ++i)
{
// 投影到单位球体上。
XMVECTOR n = XMVector3Normalize(XMLoadFloat3(&meshData.Vertices[i].Position));
// 投影到球体上。
XMVECTOR p = radius*n;
XMStoreFloat3(&meshData.Vertices[i].Position, p);
XMStoreFloat3(&meshData.Vertices[i].Normal, n);
// 从球坐标导出纹理坐标。
float theta = atan2f(meshData.Vertices[i].Position.z, meshData.Vertices[i].Position.x);
// 输入[0,2pi]。
if(theta < 0.0f)
theta += XM_2PI;
float phi = acosf(meshData.Vertices[i].Position.y / radius);
meshData.Vertices[i].TexC.x = theta/XM_2PI;
meshData.Vertices[i].TexC.y = phi/XM_PI;
// P关于theta的偏导数
meshData.Vertices[i].TangentU.x = -radius*sinf(phi)*sinf(theta);
meshData.Vertices[i].TangentU.y = 0.0f;
meshData.Vertices[i].TangentU.z = +radius*sinf(phi)*cosf(theta);
XMVECTOR T = XMLoadFloat3(&meshData.Vertices[i].TangentU);
XMStoreFloat3(&meshData.Vertices[i].TangentU, XMVector3Normalize(T));
}
return meshData;
}
绘制几何体示例
我们已经创建一个由FrameResource类型元素所构成的向量,每个FrameReousrce中都有上传缓冲区,用于为场景中每一个渲染项存储渲染过程常量和物体常量数据。
如果有3个帧资源和n个渲染项,那么我们需要创建3n个物体常量缓冲区(object constant buffer)以及3个渲染过程常量缓冲区(pass 从constant buffer),因此我们需要创建3(n+1)个常量缓冲区视图。所以我们要修改CBV堆以容纳额外的描述符
#include "../Common/UploadBuffer.h"
#include "DXApp.h"
#include "../Common/MathHelper.h"
#include "GeometryGenerator.h"
#include "FrameResource.h"
using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;
const int gNumFrameResources = 3;
struct RenderItem
{
RenderItem() = default;
// 描述了局部空间相对于世界空间的矩阵
//定义了物体位于世界空间的位置,朝向和大小
XMFLOAT4X4 World = MathHelper::Identity4x4();
// 用dirty flag来标志物体的相关数据已经发生改变,意味着我们次数需要更新常量缓冲区
// 由于每个帧资源中都有一个物体常量缓冲区,所以需要对每个帧资源进行更新
//即,当修改物体数据时,应当按NumFramesDirty= gNumFrameReousreces进行设置,从而使每个帧资源都更新
int NumFramesDirty = gNumFrameResources;
//索引到此渲染项目对应于ObjectCB的GPU常量缓冲区。
UINT ObjCBIndex = -1;
MeshGeometry* Geo = nullptr;
//图元拓扑
D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
// DrawIndexedInstanced方法的参数
UINT IndexCount = 0;
UINT StartIndexLocation = 0;
int BaseVertexLocation = 0;
};
class ShapesApp : public DXApp
{
public:
ShapesApp(HINSTANCE hInstance);
ShapesApp(const ShapesApp& rhs) = delete;
ShapesApp& operator=(const ShapesApp& rhs) = delete;
~ShapesApp();
virtual bool Initialize()override;
private:
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 OnKeyboardInput(const GameTimer& gt);
void UpdateCamera(const GameTimer& gt);
void UpdateObjectCBs(const GameTimer& gt);
void UpdateMainPassCB(const GameTimer& gt);
void BuildDescriptorHeaps();
void BuildConstantBufferViews();
void BuildRootSignature();
void BuildShadersAndInputLayout();
void BuildShapeGeometry();
void BuildPSOs();
void BuildFrameResources();
void BuildRenderItems();
void DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems);
private:
std::vector<std::unique_ptr<FrameResource>> mFrameResources;//帧资源数组
FrameResource* mCurrFrameResource = nullptr;//当前帧资源
int mCurrFrameResourceIndex = 0;//当前帧资源序号
`ComPtr<ID3D12RootSignature>` mRootSignature = nullptr;//根签名
`ComPtr<ID3D12DescriptorHeap>` mCbvHeap = nullptr;
`ComPtr<ID3D12DescriptorHeap>` mSrvDescriptorHeap = nullptr;
std::unordered_map<std::string, std::unique_ptr<MeshGeometry>> mGeometries;//几何体哈希表
std::unordered_map<std::string, `ComPtr<ID3DBlob>`> mShaders;//着色器表
std::unordered_map<std::string, `ComPtr<ID3D12PipelineState>`> mPSOs;//流水线对象表
std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
std::vector<std::unique_ptr<RenderItem>> mAllRitems;
std::vector<RenderItem*> mOpaqueRitems;
PassConstants mMainPassCB;
UINT mPassCbvOffset = 0;
bool mIsWireframe = false;
XMFLOAT3 mEyePos = { 0.0f, 0.0f, 0.0f };
XMFLOAT4X4 mView = MathHelper::Identity4x4();
XMFLOAT4X4 mProj = MathHelper::Identity4x4();
float mTheta = 1.5f*XM_PI;
float mPhi = 0.2f*XM_PI;
float mRadius = 15.0f;
POINT mLastMousePos;
};
ShapesApp::ShapesApp(HINSTANCE hInstance):DXApp(hInstance)
{
}
ShapesApp::~ShapesApp()
{
if (md3dDevice!=nullptr)
{
FlushCommandQueue();
}
}
bool ShapesApp::Initialize()
{
if (!DXApp::Initialize())
{
return false;
}
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
BuildRootSignature();
BuildShadersAndInputLayout();
BuildShapeGeometry();
BuildRenderItems();
BuildFrameResources();
BuildDescriptorHeaps();
BuildConstantBufferViews();
BuildPSOs();
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
FlushCommandQueue();
return true;
return true;
}
void ShapesApp::OnResize()
{
DXApp::OnResize();
XMMATRIX P = XMMatrixPerspectiveFovLH(0.25f*MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
XMStoreFloat4x4(&mProj, P);
}
void ShapesApp::Update(const GameTimer & gt)
{
OnKeyboardInput(gt);
UpdateCamera(gt);
mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
//GPU是否执行完所有命令?
//如果还没有就令CPU等待,直到GPU完成所有命令的执行并且抵达这个围栏点
if (mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
UpdateObjectCBs(gt);
UpdateMainPassCB(gt);
}
void ShapesApp::Draw(const GameTimer & gt)
{
auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;
// Reuse the memory associated with command recording.
// We can only reset when the associated command lists have finished execution on the GPU.
ThrowIfFailed(cmdListAlloc->Reset());
// A command list can be reset after it has been added to the command queue via ExecuteCommandList.
// Reusing the command list reuses memory.
if (mIsWireframe)
{
ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque_wireframe"].Get()));
}
else
{
ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));
}
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);
// 指定我们要渲染到的缓冲区。
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
ID3D12DescriptorHeap* descriptorHeaps[] = { mCbvHeap.Get() };
mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
int passCbvIndex = mPassCbvOffset + mCurrFrameResourceIndex;
auto passCbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
passCbvHandle.Offset(passCbvIndex, mCbvSrvUavDescriptorSize);
mCommandList->SetGraphicsRootDescriptorTable(1, passCbvHandle);
DrawRenderItems(mCommandList.Get(), mOpaqueRitems);
// 指示资源使用情况的状态转换。
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
ThrowIfFailed(mSwapChain->Present(0, 0));
mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;
// 扩展围栏值以将命令标记到该围栏点。
mCurrFrameResource->Fence = ++mCurrentFence;
// 将指令添加到命令队列以设置新的围栏点。
//因为我们在GPU时间轴上,所以新的隔离点将不会
//设置直到GPU完成处理此Signal()之前的所有命令为止。
mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
void ShapesApp::OnMouseDown(WPARAM btnState, int x, int y)
{
mLastMousePos.x = x;
mLastMousePos.y = y;
SetCapture(mhMainWnd);
}
void ShapesApp::OnMouseUp(WPARAM btnState, int x, int y)
{
ReleaseCapture();
}
void ShapesApp::OnMouseMove(WPARAM btnState, int x, int y)
{
if ((btnState & MK_LBUTTON) != 0)
{
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);
}
else if ((btnState & MK_RBUTTON) != 0)
{
float dx = 0.05f*static_cast<float>(x - mLastMousePos.x);
float dy = 0.05f*static_cast<float>(y - mLastMousePos.y);
mRadius += dx - dy;
mRadius = MathHelper::Clamp(mRadius, 5.0f, 150.0f);
}
mLastMousePos.x = x;
mLastMousePos.y = y;
}
void ShapesApp::OnKeyboardInput(const GameTimer& gt)
{
if (GetAsyncKeyState('1') & 0x8000)
mIsWireframe = true;
else
mIsWireframe = false;
}
void ShapesApp::UpdateCamera(const GameTimer& gt)
{
mEyePos.x = mRadius * sinf(mPhi)*cosf(mTheta);
mEyePos.z = mRadius * sinf(mPhi)*sinf(mTheta);
mEyePos.y = mRadius * cosf(mPhi);
// Build the view matrix.
XMVECTOR pos = XMVectorSet(mEyePos.x, mEyePos.y, mEyePos.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);
}
void ShapesApp::UpdateObjectCBs(const GameTimer& gt)
{
auto currObjectCB = mCurrFrameResource->ObjectCB.get();
for (auto& e : mAllRitems)
{
//仅在常量更改后才更新cbuffer数据。
//需要针对每个框架资源进行跟踪。
if (e->NumFramesDirty > 0)
{
XMMATRIX world = XMLoadFloat4x4(&e->World);
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
currObjectCB->CopyData(e->ObjCBIndex, objConstants);
e->NumFramesDirty--;
}
}
}
void ShapesApp::UpdateMainPassCB(const GameTimer& gt)
{
XMMATRIX view = XMLoadFloat4x4(&mView);
XMMATRIX proj = XMLoadFloat4x4(&mProj);
XMMATRIX viewProj = XMMatrixMultiply(view, proj);
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
mMainPassCB.EyePosW = mEyePos;
mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
mMainPassCB.NearZ = 1.0f;
mMainPassCB.FarZ = 1000.0f;
mMainPassCB.TotalTime = gt.TotalTime();
mMainPassCB.DeltaTime = gt.DeltaTime();
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(0, mMainPassCB);
}
/*我们已经创建一个由FrameResource类型元素所构成的向量,每个FrameReousrce中都有上传缓冲区,用于为场景中每一个渲染项存储渲染过程常量和物体常量数据。
如果有3个帧资源和n个渲染项,那么我们需要创建3n个物体常量缓冲区(object constant buffer)以及3个渲染过程常量缓冲区(pass 从constant buffer),因此我们需要创建3(n+1)个常量缓冲区视图。所以我们要修改CBV堆以容纳额外的描述符*/
void ShapesApp::BuildDescriptorHeaps()
{
UINT objCount = (UINT)mOpaqueRitems.size();
//每个帧资源的每个对象都需要一个CBV描述符,每个帧资源的perPass CBV +1。
UINT numDescriptors = (objCount+1)*gNumFrameResources;
//将偏移量保存到通过CBV的起点。 这是最后3个描述符。
mPassCbvOffset=objCount*gNumFrameResources;
//创建描述符堆
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
cbvHeapDesc.NumDescriptors = numDescriptors;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,
IID_PPV_ARGS(&mCbvHeap)));
}
//我们可以使用下列代码来填充CBV堆了,其中0-n-1描述符为第0个帧资源的物体CBV……3n、3n + 1、3n + 2分别为帧资源的渲染过程CBV
void ShapesApp::BuildConstantBufferViews()
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
UINT objCount = (UINT)mOpaqueRitems.size();
//每个帧资源的每个对象都需要一个CBV描述符。
for (int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
{
auto objectCB = mFrameResources[frameIndex]->ObjectCB->Resource();
for (UINT i = 0; i < objCount; ++i)
{
D3D12_GPU_VIRTUAL_ADDRESS cbAddress = objectCB->GetGPUVirtualAddress();
//缓冲区中第i个对象常量缓冲区的偏移量。
cbAddress += i * objCBByteSize;
//偏移量到描述符堆中的对象cbv。
int heapIndex = frameIndex * objCount + i;
auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
//通过调用ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart方法可以获得堆中的第一个描述符的句柄,但是现在堆中存储的不是一个描述符了,如果想要访问到堆中的其他描述符,必须进行偏移
handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cbAddress;
cbvDesc.SizeInBytes = objCBByteSize;
md3dDevice->CreateConstantBufferView(&cbvDesc, handle);
}
}
UINT passCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(PassConstants));
//最后三个描述符是每个帧资源的传递CBV。
for (int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
{
auto passCB = mFrameResources[frameIndex]->PassCB->Resource();
D3D12_GPU_VIRTUAL_ADDRESS cbAddress = passCB->GetGPUVirtualAddress();
// 偏移到描述符堆中的传递cbv。
int heapIndex = mPassCbvOffset + frameIndex;
auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cbAddress;
cbvDesc.SizeInBytes = passCBByteSize;
md3dDevice->CreateConstantBufferView(&cbvDesc, handle);
}
}
void ShapesApp::BuildRootSignature()
{
// 创建2个描述
CD3DX12_DESCRIPTOR_RANGE cbvTable0;
cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
CD3DX12_DESCRIPTOR_RANGE cbvTable1;
cbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);
// 2个成员的描述符表
CD3DX12_ROOT_PARAMETER slotRootParameter[2];
// Create root CBVs.
slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
slotRootParameter[1].InitAsDescriptorTable(1, &cbvTable1);
// 创建根CBV
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
`ComPtr<ID3DBlob>` serializedRootSig = nullptr;
`ComPtr<ID3DBlob>` errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if (errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
void ShapesApp::BuildShadersAndInputLayout()
{
mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\color7.hlsl", nullptr, "VS", "vs_5_1");
mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\color7.hlsl", nullptr, "PS", "ps_5_1");
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 },
};
}
void ShapesApp::BuildShapeGeometry()
{
GeometryGenerator geoGen;
GeometryGenerator::MeshData box = geoGen.CreateBox(1.5f, 0.5f, 1.5f, 3);
GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
GeometryGenerator::MeshData cylinder = geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);
//顶点偏移量缓存到级联的顶点缓冲区中的每个对象。
UINT boxVertexOffset = 0;
UINT gridVertexOffset = (UINT)box.Vertices.size();
UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();
//将每个对象的起始索引缓存在串联索引缓冲区中。
UINT boxIndexOffset = 0;
UINT gridIndexOffset = (UINT)box.Indices32.size();
UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();
SubmeshGeometry boxSubmesh;
boxSubmesh.IndexCount = (UINT)box.Indices32.size();
boxSubmesh.StartIndexLocation = boxIndexOffset;
boxSubmesh.BaseVertexLocation = boxVertexOffset;
SubmeshGeometry gridSubmesh;
gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
gridSubmesh.StartIndexLocation = gridIndexOffset;
gridSubmesh.BaseVertexLocation = gridVertexOffset;
SubmeshGeometry sphereSubmesh;
sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
sphereSubmesh.StartIndexLocation = sphereIndexOffset;
sphereSubmesh.BaseVertexLocation = sphereVertexOffset;
SubmeshGeometry cylinderSubmesh;
cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;
//顶点数总计
auto totalVertexCount =
box.Vertices.size() +
grid.Vertices.size() +
sphere.Vertices.size() +
cylinder.Vertices.size();
std::vector<Vertex> vertices(totalVertexCount);
//添加所有顶点和颜色数据
UINT k = 0;
for (size_t i = 0; i < box.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = box.Vertices[i].Position;
vertices[k].Color = XMFLOAT4(DirectX::Colors::Blue);
}
for (size_t i = 0; i < grid.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = grid.Vertices[i].Position;
vertices[k].Color = XMFLOAT4(DirectX::Colors::ForestGreen);
}
for (size_t i = 0; i < sphere.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = sphere.Vertices[i].Position;
vertices[k].Color = XMFLOAT4(DirectX::Colors::Crimson);
}
for (size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = cylinder.Vertices[i].Position;
vertices[k].Color = XMFLOAT4(DirectX::Colors::SteelBlue);
}
//添加所有索引
std::vector<std::uint16_t> indices;
indices.insert(indices.end(), std::begin(box.GetIndices16()), std::end(box.GetIndices16()));
indices.insert(indices.end(), std::begin(grid.GetIndices16()), std::end(grid.GetIndices16()));
indices.insert(indices.end(), std::begin(sphere.GetIndices16()), std::end(sphere.GetIndices16()));
indices.insert(indices.end(), std::begin(cylinder.GetIndices16()), std::end(cylinder.GetIndices16()));
const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = "shapeGeo";
ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
geo->DrawArgs["box"] = boxSubmesh;
geo->DrawArgs["grid"] = gridSubmesh;
geo->DrawArgs["sphere"] = sphereSubmesh;
geo->DrawArgs["cylinder"] = cylinderSubmesh;
mGeometries[geo->Name] = std::move(geo);
}
void ShapesApp::BuildPSOs()
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
opaquePsoDesc.pRootSignature = mRootSignature.Get();
opaquePsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
mShaders["standardVS"]->GetBufferSize()
};
opaquePsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
mShaders["opaquePS"]->GetBufferSize()
};
opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
opaquePsoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
opaquePsoDesc.SampleMask = UINT_MAX;
opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
opaquePsoDesc.NumRenderTargets = 1;
opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
opaquePsoDesc.DSVFormat = mDepthStencilFormat;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
D3D12_GRAPHICS_PIPELINE_STATE_DESC opaqueWireframePsoDesc = opaquePsoDesc;
opaqueWireframePsoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;//网格显示
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaqueWireframePsoDesc, IID_PPV_ARGS(&mPSOs["opaque_wireframe"])));
}
void ShapesApp::BuildFrameResources()
{
for (int i = 0; i < gNumFrameResources; ++i)
{
mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
1, (UINT)mAllRitems.size()));
}
}
void ShapesApp::BuildRenderItems()
{
auto boxRitem = std::make_unique<RenderItem>();
XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f)*XMMatrixTranslation(0.0f, 0.5f, 0.0f));
boxRitem->ObjCBIndex = 0;
boxRitem->Geo = mGeometries["shapeGeo"].get();
boxRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
boxRitem->StartIndexLocation = boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
boxRitem->BaseVertexLocation = boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;
mAllRitems.push_back(std::move(boxRitem));
auto gridRitem = std::make_unique<RenderItem>();
gridRitem->World = MathHelper::Identity4x4();
gridRitem->ObjCBIndex = 1;
gridRitem->Geo = mGeometries["shapeGeo"].get();
gridRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
mAllRitems.push_back(std::move(gridRitem));
UINT objCBIndex = 2;
for (int i = 0; i < 5; ++i)
{
auto leftCylRitem = std::make_unique<RenderItem>();
auto rightCylRitem = std::make_unique<RenderItem>();
auto leftSphereRitem = std::make_unique<RenderItem>();
auto rightSphereRitem = std::make_unique<RenderItem>();
XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
XMMATRIX rightCylWorld = XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);
XMMATRIX leftSphereWorld = XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
XMMATRIX rightSphereWorld = XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);
XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
leftCylRitem->ObjCBIndex = objCBIndex++;
leftCylRitem->Geo = mGeometries["shapeGeo"].get();
leftCylRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
leftCylRitem->IndexCount = leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
leftCylRitem->StartIndexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
leftCylRitem->BaseVertexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
rightCylRitem->ObjCBIndex = objCBIndex++;
rightCylRitem->Geo = mGeometries["shapeGeo"].get();
rightCylRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
rightCylRitem->IndexCount = rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
rightCylRitem->StartIndexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
rightCylRitem->BaseVertexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
leftSphereRitem->ObjCBIndex = objCBIndex++;
leftSphereRitem->Geo = mGeometries["shapeGeo"].get();
leftSphereRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
leftSphereRitem->IndexCount = leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
leftSphereRitem->StartIndexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
leftSphereRitem->BaseVertexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;
XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
rightSphereRitem->ObjCBIndex = objCBIndex++;
rightSphereRitem->Geo = mGeometries["shapeGeo"].get();
rightSphereRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
rightSphereRitem->IndexCount = rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
rightSphereRitem->StartIndexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
rightSphereRitem->BaseVertexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;
mAllRitems.push_back(std::move(leftCylRitem));
mAllRitems.push_back(std::move(rightCylRitem));
mAllRitems.push_back(std::move(leftSphereRitem));
mAllRitems.push_back(std::move(rightSphereRitem));
}
// All the render items are opaque.
for (auto& e : mAllRitems)
mOpaqueRitems.push_back(e.get());
}
//在该步骤中,最复杂的一个部分便是为了绘制物体而根据偏移量找到它在描述符堆中对应的CBV了:
void ShapesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
auto objectCB = mCurrFrameResource->ObjectCB->Resource();
//遍历RenderItem
for (size_t i = 0; i < ritems.size(); ++i)
{
auto ri = ritems[i];
cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
// 此对象和此帧资源的描述符堆中的CBV偏移。
UINT cbvIndex = mCurrFrameResourceIndex * (UINT)mOpaqueRitems.size() + ri->ObjCBIndex;
auto cbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
cbvHandle.Offset(cbvIndex, mCbvSrvUavDescriptorSize);
cmdList->SetGraphicsRootDescriptorTable(0, cbvHandle);
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
}
}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
try
{
ShapesApp theApp(hInstance);
if (!theApp.Initialize())
return 0;
return theApp.Run();
}
catch (DxException& e)
{
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}
根签名
根参数
typedef struct D3D12_ROOT_PARAMETER
{
D3D12_ROOT_PARAMETER_TYPE ParameterType;
union
{
D3D12_ROOT_DESCRIPTOR_TABLE DescriptorTable;//指定类型,如描述符表,根常量,CBV,SRV,UAV
D3D12_ROOT_CONSTANTS Constants;
D3D12_ROOT_DESCRIPTOR Descriptor;
} ;
D3D12_SHADER_VISIBILITY ShaderVisibility;//指定着色器可见性
} D3D12_ROOT_PARAMETER;
typedef
enum D3D12_ROOT_PARAMETER_TYPE
{
D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE = 0,
D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS = ( D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE + 1 ) ,
D3D12_ROOT_PARAMETER_TYPE_CBV = ( D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS + 1 ) ,
D3D12_ROOT_PARAMETER_TYPE_SRV = ( D3D12_ROOT_PARAMETER_TYPE_CBV + 1 ) ,
D3D12_ROOT_PARAMETER_TYPE_UAV = ( D3D12_ROOT_PARAMETER_TYPE_SRV + 1 )
} D3D12_ROOT_PARAMETER_TYPE;
- 描述符表:引用的是描述符堆种的一块连续范围,用于确定要绑定的资源.每个描述符表占用1DWORD
- 跟描述符:通过直接设置跟描述符即可指示要绑定的资源,而且无需将它存于描述符中,但是只有常量缓冲区
CBV,以及缓冲区的SRV/UAV才可以根据描述符的身份进行绑定.而纹理SRV不可以.每个描述符占用2DWORD - 根常量:借助根常量直接绑定一系列的32位的常量值.每个常量32位,占用1个DWORD
描述符表
typedef struct D3D12_DESCRIPTOR_RANGE
{
D3D12_DESCRIPTOR_RANGE_TYPE RangeType;//类型,如...SRV,...CBV
UINT NumDescriptors;//描述符的数量
UINT BaseShaderRegister;//绑定的寄存器槽
UINT RegisterSpace;//空间,默认=0
UINT OffsetInDescriptorsFromTableStart;//此描述符距离表其实地址的偏移量
}
CD3D12_DESCRIPTOR_RANGE是封装了一些便捷方法,通过Init方法初始化
如果NumDescriptors设置为3,BaseShaderRegister设置为1,类型为CBV,那么对应的HLSL就是
cbuffer cbA : register(b1){};//从1开始
cbuffer cbB : register(b2){};
cbuffer cbC : register(b3){};
- 示例
CD3DX12_DESCRIPTOR_RANGE descRange[3];
descRange[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 2, 0, 0);
descRange[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 3, 0, 2/*起始偏移量*/);
descRange[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 5/*起始偏移量*/);
slotRootParameter[0].InitAsDescriptorTable(3, descRange, D3D12_SHADER_VISIBILITY_ALL);
根描述符
填写D3D12_ROOT_PARAMETER中的Descriptor,可以定义为根描述符
typedef struct D3D12_ROOT_DESCRIPTOR
{
UINT ShaderRegister;
UINT RegisterSpace;
} D3D12_ROOT_DESCRIPTOR;
- ShaderRegister:如果设置为2,类型为CBV,那么对应的常量缓冲区是
register(b2) - RegisterSpace:空间
与描述符表需要在描述符堆中设置对应的描述符句柄不同,根描述符只需要直接绑定资源的虚拟地址就可以
示例
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress();
objCBAddress += ri->ObjCBIndex*objCBByteSize;
cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
根常量
typedef struct D3D12_ROOT_CONSTANTS
{
UINT ShaderRegister;
UINT RegisterSpace;
UINT Num32BitValues;//根常量所需的32位常量的个数
} D3D12_ROOT_CONSTANTS;
- 示例
CD3DX12_ROOT_PARAMETER slotRootParameter[1];
slotRootParameter[0].InitAsConstants(12,0);
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
//应用程序部分
auto weights = CalcGaussWeights(2.5f);
int blurRadius = (int)weights.size()/2;
cmdList->SetGraphicsRoot32BitConstant(0,1,&blurRadius,0);
cmdList->SetGraphicsRoot32BitConstant(0,(UINT)weights.size(),weights.data(),1);
- HLSL代码
cbuffer cbSetting : register(b0)
{
int gBlurRadius;
//11种模糊值
float w0;
float w1;
float w2;
float w3;
float w4;
float w5;
float w6;
float w7;
float w8;
float w9;
float w10;
};
与根描述符一i杨,根常量无需涉及描述符堆
根签名复杂示例
- 着色器
Texture2D gDiffuseMap : register(t0);
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
float4x4 gTexTransform;
};
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
//....
};
cbuffer cbMaterial : register(b2)
{
float4 gDiffuseAlbedo;
float3 gFresnelR0;
float gRoughness;
float4x4 gMatTransform;
};
- 根签名
CD3DX12_DESCRIPTOR_RANGE texTable;
texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV,1,0);
CD3DX12_ROOT_PARAMETER slotRootParameter[4];
slotRootParameter[0].InitAsDescriptorTable(1, texTable, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParameter[1].InitAsConstantBufferView(0);//寄存器b0
slotRootParameter[2].InitAsConstantBufferView(1);//寄存器b1
slotRootParameter[3].InitAsConstantBufferView(2);//寄存器b2
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
陆地与波浪演示程序
栅格坐标
构建一个
m*n个顶点组成的栅格,意味着具有(m-1)*(n-1)个四边形,即2倍的三角形如果宽度为
w,深度为d,那么x和y轴方向上的间距分别为dx=w/(n-1)和dz=d/(m-1)那么第
i行,第j列的最坐标为Vij=[-0.5w+j*dx, 0.0, 0.5d-i*dz]
uint32 vertexCount = m*n;
uint32 faceCount = (m-1)*(n-1)*2;
float halfWidth = 0.5f*width;
float halfDepth = 0.5f*depth;
float dx = width / (n-1);
float dz = depth / (m-1);
float du = 1.0f / (n-1);
float dv = 1.0f / (m-1);
meshData.Vertices.resize(vertexCount);
for(uint32 i = 0; i < m; ++i)
{
float z = halfDepth - i*dz;
for(uint32 j = 0; j < n; ++j)
{
float x = -halfWidth + j*dx;
meshData.Vertices[i*n+j].Position = XMFLOAT3(x, 0.0f, z);
meshData.Vertices[i*n+j].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f);
meshData.Vertices[i*n+j].TangentU = XMFLOAT3(1.0f, 0.0f, 0.0f);
meshData.Vertices[i*n+j].TexC.x = j*du;
meshData.Vertices[i*n+j].TexC.y = i*dv;
}
}
栅格索引
三角形ABC的索引(i*n+j, i*n+j+1, (i+1)*n+j)
三角形CBD的索引((i+1)*n+j, i*n+j+1, (i+1)*n+j+1)
meshData.Indices32.resize(faceCount*3); // 3 indices per face
uint32 k = 0;
for(uint32 i = 0; i < m-1; ++i)
{
for(uint32 j = 0; j < n-1; ++j)
{
meshData.Indices32[k] = i*n+j;
meshData.Indices32[k+1] = i*n+j+1;
meshData.Indices32[k+2] = (i+1)*n+j;
meshData.Indices32[k+3] = (i+1)*n+j;
meshData.Indices32[k+4] = i*n+j+1;
meshData.Indices32[k+5] = (i+1)*n+j+1;
k += 6; // next quad
}
}

构建山体
void LandAndWavesApp::BuildWavesGeometryBuffers()
{
std::vector<std::uint16_t> indices(3 * mWaves->TriangleCount()); // 3 indices per face
assert(mWaves->VertexCount() < 0x0000ffff);
// Iterate over each quad.
int m = mWaves->RowCount();
int n = mWaves->ColumnCount();
int k = 0;
for(int i = 0; i < m - 1; ++i)
{
for(int j = 0; j < n - 1; ++j)
{
indices[k] = i*n + j;
indices[k + 1] = i*n + j + 1;
indices[k + 2] = (i + 1)*n + j;
indices[k + 3] = (i + 1)*n + j;
indices[k + 4] = i*n + j + 1;
indices[k + 5] = (i + 1)*n + j + 1;
k += 6; // next quad
}
}
UINT vbByteSize = mWaves->VertexCount()*sizeof(Vertex);
UINT ibByteSize = (UINT)indices.size()*sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = "waterGeo";
// Set dynamically.
geo->VertexBufferCPU = nullptr;
geo->VertexBufferGPU = nullptr;
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
SubmeshGeometry submesh;
submesh.IndexCount = (UINT)indices.size();
submesh.StartIndexLocation = 0;
submesh.BaseVertexLocation = 0;
geo->DrawArgs["grid"] = submesh;
mGeometries["waterGeo"] = std::move(geo);
}
动态顶点缓冲区
动态顶点缓冲区即有可以频繁修改其中顶点数据的资源
如我们可以得到随着时间流走根性三角形的顶点高度,将此数据创建为动态顶点缓冲区
另外比如执行复杂的物理模拟和碰撞检测的粒子系统也需要用到
完整代码
- waves
#pragma once
#ifndef WAVES_H
#define WAVES_H
#include <vector>
#include <DirectXMath.h>
class Waves
{
public:
Waves(int m, int n, float dx, float dt, float speed, float damping);
Waves(const Waves& rhs) = delete;
Waves& operator=(const Waves& rhs) = delete;
~Waves();
int RowCount()const;
int ColumnCount()const;
int VertexCount()const;
int TriangleCount()const;
float Width()const;
float Depth()const;
// 第i个点的坐标
const DirectX::XMFLOAT3& Position(int i)const { return mCurrSolution[i]; }
// 第i个点的法线
const DirectX::XMFLOAT3& Normal(int i)const { return mNormals[i]; }
// 第i个点的切线
const DirectX::XMFLOAT3& TangentX(int i)const { return mTangentX[i]; }
void Update(float dt);
void Disturb(int i, int j, float magnitude);
private:
int mNumRows = 0;//行数
int mNumCols = 0;//列数
int mVertexCount = 0;
int mTriangleCount = 0;
//预先计算仿真常数。
float mK1 = 0.0f;
float mK2 = 0.0f;
float mK3 = 0.0f;
float mTimeStep = 0.0f;//时间步长
float mSpatialStep = 0.0f;//空间步长
std::vector<DirectX::XMFLOAT3> mPrevSolution;//上一个解决方案
std::vector<DirectX::XMFLOAT3> mCurrSolution;//当前解决方案
std::vector<DirectX::XMFLOAT3> mNormals;
std::vector<DirectX::XMFLOAT3> mTangentX;
};
#endif
#include "Waves.h"
#include <DirectXMath.h>
#include <ppl.h>
using namespace DirectX;
Waves::Waves(int m, int n, float dx, float dt, float speed, float damping)
{
mNumRows = m;
mNumCols = n;
mVertexCount = m * n;
mTriangleCount = (m - 1)*(n - 1) * 2;
mTimeStep = dt;
mSpatialStep = dx;
float d = damping * dt + 2.0f;
float e = (speed*speed)*(dt*dt) / (dx*dx);
mK1 = (damping*dt - 2.0f) / d;
mK2 = (4.0f - 8.0f*e) / d;
mK3 = (2.0f*e) / d;
mPrevSolution.resize(m*n);
mCurrSolution.resize(m*n);
mNormals.resize(m*n);
mTangentX.resize(m*n);
float halfWidth = (n - 1)*dx*0.5f;
float halfDepth = (m - 1)*dx*0.5f;
for (int i = 0; i < m; ++i)
{
float z = halfDepth - i * dx;
for (int j = 0; j < n; ++j)
{
float x = -halfWidth + j * dx;
mPrevSolution[i*n + j] = XMFLOAT3(x, 0.0f, z);
mCurrSolution[i*n + j] = XMFLOAT3(x, 0.0f, z);
mNormals[i*n + j] = XMFLOAT3(0.0f, 1.0f, 0.0f);
mTangentX[i*n + j] = XMFLOAT3(1.0f, 0.0f, 0.0f);
}
}
}
Waves::~Waves()
{
}
int Waves::RowCount() const
{
return mNumRows;
}
int Waves::ColumnCount() const
{
return mNumCols;
}
int Waves::VertexCount() const
{
return mVertexCount;
}
int Waves::TriangleCount() const
{
return mTriangleCount;
}
float Waves::Width() const
{
return mNumRows*mSpatialStep;
}
float Waves::Depth() const
{
return mNumCols*mSpatialStep;
}
void Waves::Update(float dt)
{
static float t =0;
if (t>=mTimeStep)
{
concurrency::parallel_for(1, mNumRows - 1, [this](int i)
{
//此更新后,我们将丢弃旧的旧版本缓冲区,因此请使用新的更新覆盖该缓冲区。
//注意我们如何就地执行此操作(读/写同一元素)
//因为我们不再需要prev_ij,并且赋值最后发生。
//注意j索引x和i索引z:h(x_j,z_i,t_k)
//此外,我们的+ z轴会“向下”; 这只是为了与行索引下降保持一致。
for (int j = 1; j < mNumCols - 1; ++j)
{
mPrevSolution[i*mNumCols + j].y =
mK1 * mPrevSolution[i*mNumCols + j].y +
mK2 * mCurrSolution[i*mNumCols + j].y +
mK3 * (mCurrSolution[(i + 1)*mNumCols + j].y +
mCurrSolution[(i - 1)*mNumCols + j].y +
mCurrSolution[i*mNumCols + j + 1].y +
mCurrSolution[i*mNumCols + j - 1].y);
}
});
//我们只是用新数据覆盖了先前的缓冲区,所以此数据需要成为当前的解决方案和旧的
//当前解决方案成为新的先前解决方案。
std::swap(mPrevSolution, mCurrSolution);
t = 0.0f;
//计算法线
concurrency::parallel_for(1, mNumRows - 1, [this](int i)
//for(int i = 1; i < mNumRows - 1; ++i)
{
for (int j = 1; j < mNumCols - 1; ++j)
{
float l = mCurrSolution[i*mNumCols + j - 1].y;
float r = mCurrSolution[i*mNumCols + j + 1].y;
float t = mCurrSolution[(i - 1)*mNumCols + j].y;
float b = mCurrSolution[(i + 1)*mNumCols + j].y;
mNormals[i*mNumCols + j].x = -r + l;
mNormals[i*mNumCols + j].y = 2.0f*mSpatialStep;
mNormals[i*mNumCols + j].z = b - t;
XMVECTOR n = XMVector3Normalize(XMLoadFloat3(&mNormals[i*mNumCols + j]));
XMStoreFloat3(&mNormals[i*mNumCols + j], n);
mTangentX[i*mNumCols + j] = XMFLOAT3(2.0f*mSpatialStep, r - l, 0.0f);
XMVECTOR T = XMVector3Normalize(XMLoadFloat3(&mTangentX[i*mNumCols + j]));
XMStoreFloat3(&mTangentX[i*mNumCols + j], T);
}
});
}
}
void Waves::Disturb(int i, int j, float magnitude)
{
assert(i > 1 && i < mNumRows - 2);
assert(j > 1 && j < mNumCols - 2);
float halfMag = 0.5f*magnitude;
// 绕顶第i个顶点高度及其相邻点。
mCurrSolution[i*mNumCols + j].y += magnitude;
mCurrSolution[i*mNumCols + j + 1].y += halfMag;
mCurrSolution[i*mNumCols + j - 1].y += halfMag;
mCurrSolution[(i + 1)*mNumCols + j].y += halfMag;
mCurrSolution[(i - 1)*mNumCols + j].y += halfMag;
}
- LandAndWaves
//***************************************************************************************
// LandAndWavesApp.cpp by Frank Luna (C) 2015 All Rights Reserved.
//
// Hold down '1' key to view scene in wireframe mode.
//***************************************************************************************
#include "../../Common/d3dApp.h"
#include "../../Common/MathHelper.h"
#include "../../Common/UploadBuffer.h"
#include "../../Common/GeometryGenerator.h"
#include "FrameResource.h"
#include "Waves.h"
using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;
const int gNumFrameResources = 3;
// Lightweight structure stores parameters to draw a shape. This will
// vary from app-to-app.
struct RenderItem
{
RenderItem() = default;
// World matrix of the shape that describes the object's local space
// relative to the world space, which defines the position, orientation,
// and scale of the object in the world.
XMFLOAT4X4 World = MathHelper::Identity4x4();
// Dirty flag indicating the object data has changed and we need to update the constant buffer.
// Because we have an object cbuffer for each FrameResource, we have to apply the
// update to each FrameResource. Thus, when we modify obect data we should set
// NumFramesDirty = gNumFrameResources so that each frame resource gets the update.
int NumFramesDirty = gNumFrameResources;
// Index into GPU constant buffer corresponding to the ObjectCB for this render item.
UINT ObjCBIndex = -1;
MeshGeometry* Geo = nullptr;
// Primitive topology.
D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
// DrawIndexedInstanced parameters.
UINT IndexCount = 0;
UINT StartIndexLocation = 0;
int BaseVertexLocation = 0;
};
enum class RenderLayer : int
{
Opaque = 0,
Count
};
class LandAndWavesApp : public D3DApp
{
public:
LandAndWavesApp(HINSTANCE hInstance);
LandAndWavesApp(const LandAndWavesApp& rhs) = delete;
LandAndWavesApp& operator=(const LandAndWavesApp& rhs) = delete;
~LandAndWavesApp();
virtual bool Initialize()override;
private:
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 OnKeyboardInput(const GameTimer& gt);
void UpdateCamera(const GameTimer& gt);
void UpdateObjectCBs(const GameTimer& gt);
void UpdateMainPassCB(const GameTimer& gt);
void UpdateWaves(const GameTimer& gt);
void BuildRootSignature();
void BuildShadersAndInputLayout();
void BuildLandGeometry();
void BuildWavesGeometryBuffers();
void BuildPSOs();
void BuildFrameResources();
void BuildRenderItems();
void DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems);
float GetHillsHeight(float x, float z)const;
XMFLOAT3 GetHillsNormal(float x, float z)const;
private:
std::vector<std::unique_ptr<FrameResource>> mFrameResources;
FrameResource* mCurrFrameResource = nullptr;
int mCurrFrameResourceIndex = 0;
UINT mCbvSrvDescriptorSize = 0;
`ComPtr<ID3D12RootSignature>` mRootSignature = nullptr;
std::unordered_map<std::string, std::unique_ptr<MeshGeometry>> mGeometries;
std::unordered_map<std::string, `ComPtr<ID3DBlob>`> mShaders;
std::unordered_map<std::string, `ComPtr<ID3D12PipelineState>`> mPSOs;
std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
RenderItem* mWavesRitem = nullptr;
// List of all the render items.
std::vector<std::unique_ptr<RenderItem>> mAllRitems;
// Render items divided by PSO.
std::vector<RenderItem*> mRitemLayer[(int)RenderLayer::Count];
std::unique_ptr<Waves> mWaves;
PassConstants mMainPassCB;
bool mIsWireframe = false;
XMFLOAT3 mEyePos = { 0.0f, 0.0f, 0.0f };
XMFLOAT4X4 mView = MathHelper::Identity4x4();
XMFLOAT4X4 mProj = MathHelper::Identity4x4();
float mTheta = 1.5f*XM_PI;
float mPhi = XM_PIDIV2 - 0.1f;
float mRadius = 50.0f;
float mSunTheta = 1.25f*XM_PI;
float mSunPhi = XM_PIDIV4;
POINT mLastMousePos;
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
try
{
LandAndWavesApp theApp(hInstance);
if(!theApp.Initialize())
return 0;
return theApp.Run();
}
catch(DxException& e)
{
MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
return 0;
}
}
LandAndWavesApp::LandAndWavesApp(HINSTANCE hInstance)
: D3DApp(hInstance)
{
}
LandAndWavesApp::~LandAndWavesApp()
{
if(md3dDevice != nullptr)
FlushCommandQueue();
}
bool LandAndWavesApp::Initialize()
{
if(!D3DApp::Initialize())
return false;
// Reset the command list to prep for initialization commands.
ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
mWaves = std::make_unique<Waves>(128, 128, 1.0f, 0.03f, 4.0f, 0.2f);
BuildRootSignature();
BuildShadersAndInputLayout();
BuildLandGeometry();
BuildWavesGeometryBuffers();
BuildRenderItems();
BuildRenderItems();
BuildFrameResources();
BuildPSOs();
// Execute the initialization commands.
ThrowIfFailed(mCommandList->Close());
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
// Wait until initialization is complete.
FlushCommandQueue();
return true;
}
void LandAndWavesApp::OnResize()
{
D3DApp::OnResize();
// The window resized, so update the aspect ratio and recompute the projection matrix.
XMMATRIX P = XMMatrixPerspectiveFovLH(0.25f*MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
XMStoreFloat4x4(&mProj, P);
}
void LandAndWavesApp::Update(const GameTimer& gt)
{
OnKeyboardInput(gt);
UpdateCamera(gt);
// Cycle through the circular frame resource array.
mCurrFrameResourceIndex = (mCurrFrameResourceIndex + 1) % gNumFrameResources;
mCurrFrameResource = mFrameResources[mCurrFrameResourceIndex].get();
// Has the GPU finished processing the commands of the current frame resource?
// If not, wait until the GPU has completed commands up to this fence point.
if(mCurrFrameResource->Fence != 0 && mFence->GetCompletedValue() < mCurrFrameResource->Fence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrFrameResource->Fence, eventHandle));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
UpdateObjectCBs(gt);
UpdateMainPassCB(gt);
UpdateWaves(gt);
}
void LandAndWavesApp::Draw(const GameTimer& gt)
{
auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;
// Reuse the memory associated with command recording.
// We can only reset when the associated command lists have finished execution on the GPU.
ThrowIfFailed(cmdListAlloc->Reset());
// A command list can be reset after it has been added to the command queue via ExecuteCommandList.
// Reusing the command list reuses memory.
if(mIsWireframe)
{
ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque_wireframe"].Get()));
}
else
{
ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));
}
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);
// Indicate a state transition on the resource usage.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
// Clear the back buffer and depth buffer.
mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
// Specify the buffers we are going to render to.
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
// Bind per-pass constant buffer. We only need to do this once per-pass.
auto passCB = mCurrFrameResource->PassCB->Resource();
mCommandList->SetGraphicsRootConstantBufferView(1, passCB->GetGPUVirtualAddress());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
// Indicate a state transition on the resource usage.
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
// Done recording commands.
ThrowIfFailed(mCommandList->Close());
// Add the command list to the queue for execution.
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
// Swap the back and front buffers
ThrowIfFailed(mSwapChain->Present(0, 0));
mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;
// Advance the fence value to mark commands up to this fence point.
mCurrFrameResource->Fence = ++mCurrentFence;
// Add an instruction to the command queue to set a new fence point.
// Because we are on the GPU timeline, the new fence point won't be
// set until the GPU finishes processing all the commands prior to this Signal().
mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}
void LandAndWavesApp::OnMouseDown(WPARAM btnState, int x, int y)
{
mLastMousePos.x = x;
mLastMousePos.y = y;
SetCapture(mhMainWnd);
}
void LandAndWavesApp::OnMouseUp(WPARAM btnState, int x, int y)
{
ReleaseCapture();
}
void LandAndWavesApp::OnMouseMove(WPARAM btnState, int x, int y)
{
if((btnState & MK_LBUTTON) != 0)
{
// Make each pixel correspond to a quarter of a degree.
float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));
// Update angles based on input to orbit camera around box.
mTheta += dx;
mPhi += dy;
// Restrict the angle mPhi.
mPhi = MathHelper::Clamp(mPhi, 0.1f, MathHelper::Pi - 0.1f);
}
else if((btnState & MK_RBUTTON) != 0)
{
// Make each pixel correspond to 0.2 unit in the scene.
float dx = 0.2f*static_cast<float>(x - mLastMousePos.x);
float dy = 0.2f*static_cast<float>(y - mLastMousePos.y);
// Update the camera radius based on input.
mRadius += dx - dy;
// Restrict the radius.
mRadius = MathHelper::Clamp(mRadius, 5.0f, 150.0f);
}
mLastMousePos.x = x;
mLastMousePos.y = y;
}
void LandAndWavesApp::OnKeyboardInput(const GameTimer& gt)
{
if(GetAsyncKeyState('1') & 0x8000)
mIsWireframe = true;
else
mIsWireframe = false;
}
void LandAndWavesApp::UpdateCamera(const GameTimer& gt)
{
// Convert Spherical to Cartesian coordinates.
mEyePos.x = mRadius*sinf(mPhi)*cosf(mTheta);
mEyePos.z = mRadius*sinf(mPhi)*sinf(mTheta);
mEyePos.y = mRadius*cosf(mPhi);
// Build the view matrix.
XMVECTOR pos = XMVectorSet(mEyePos.x, mEyePos.y, mEyePos.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);
}
void LandAndWavesApp::UpdateObjectCBs(const GameTimer& gt)
{
auto currObjectCB = mCurrFrameResource->ObjectCB.get();
for(auto& e : mAllRitems)
{
// Only update the cbuffer data if the constants have changed.
// This needs to be tracked per frame resource.
if(e->NumFramesDirty > 0)
{
XMMATRIX world = XMLoadFloat4x4(&e->World);
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
currObjectCB->CopyData(e->ObjCBIndex, objConstants);
// Next FrameResource need to be updated too.
e->NumFramesDirty--;
}
}
}
void LandAndWavesApp::UpdateMainPassCB(const GameTimer& gt)
{
XMMATRIX view = XMLoadFloat4x4(&mView);
XMMATRIX proj = XMLoadFloat4x4(&mProj);
XMMATRIX viewProj = XMMatrixMultiply(view, proj);
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));
XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
mMainPassCB.EyePosW = mEyePos;
mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
mMainPassCB.NearZ = 1.0f;
mMainPassCB.FarZ = 1000.0f;
mMainPassCB.TotalTime = gt.TotalTime();
mMainPassCB.DeltaTime = gt.DeltaTime();
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(0, mMainPassCB);
}
void LandAndWavesApp::UpdateWaves(const GameTimer& gt)
{
// Every quarter second, generate a random wave.
static float t_base = 0.0f;
if((mTimer.TotalTime() - t_base) >= 0.25f)
{
t_base += 0.25f;
int i = MathHelper::Rand(4, mWaves->RowCount() - 5);
int j = MathHelper::Rand(4, mWaves->ColumnCount() - 5);
float r = MathHelper::RandF(0.2f, 0.5f);
mWaves->Disturb(i, j, r);
}
// Update the wave simulation.
mWaves->Update(gt.DeltaTime());
// Update the wave vertex buffer with the new solution.
auto currWavesVB = mCurrFrameResource->WavesVB.get();
for(int i = 0; i < mWaves->VertexCount(); ++i)
{
Vertex v;
v.Pos = mWaves->Position(i);
v.Color = XMFLOAT4(DirectX::Colors::Blue);
currWavesVB->CopyData(i, v);
}
// Set the dynamic VB of the wave renderitem to the current frame VB.
mWavesRitem->Geo->VertexBufferGPU = currWavesVB->Resource();
}
void LandAndWavesApp::BuildRootSignature()
{
// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[2];
// Create root CBV.
slotRootParameter[0].InitAsConstantBufferView(0);
slotRootParameter[1].InitAsConstantBufferView(1);
// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
`ComPtr<ID3DBlob>` serializedRootSig = nullptr;
`ComPtr<ID3DBlob>` errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
void LandAndWavesApp::BuildShadersAndInputLayout()
{
mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "VS", "vs_5_0");
mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\color.hlsl", nullptr, "PS", "ps_5_0");
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 }
};
}
void LandAndWavesApp::BuildLandGeometry()
{
GeometryGenerator geoGen;
GeometryGenerator::MeshData grid = geoGen.CreateGrid(160.0f, 160.0f, 50, 50);
//
// Extract the vertex elements we are interested and apply the height function to
// each vertex. In addition, color the vertices based on their height so we have
// sandy looking beaches, grassy low hills, and snow mountain peaks.
//
std::vector<Vertex> vertices(grid.Vertices.size());
for(size_t i = 0; i < grid.Vertices.size(); ++i)
{
auto& p = grid.Vertices[i].Position;
vertices[i].Pos = p;
vertices[i].Pos.y = GetHillsHeight(p.x, p.z);
// Color the vertex based on its height.
if(vertices[i].Pos.y < -10.0f)
{
// Sandy beach color.
vertices[i].Color = XMFLOAT4(1.0f, 0.96f, 0.62f, 1.0f);
}
else if(vertices[i].Pos.y < 5.0f)
{
// Light yellow-green.
vertices[i].Color = XMFLOAT4(0.48f, 0.77f, 0.46f, 1.0f);
}
else if(vertices[i].Pos.y < 12.0f)
{
// Dark yellow-green.
vertices[i].Color = XMFLOAT4(0.1f, 0.48f, 0.19f, 1.0f);
}
else if(vertices[i].Pos.y < 20.0f)
{
// Dark brown.
vertices[i].Color = XMFLOAT4(0.45f, 0.39f, 0.34f, 1.0f);
}
else
{
// White snow.
vertices[i].Color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
}
}
const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
std::vector<std::uint16_t> indices = grid.GetIndices16();
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = "landGeo";
ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
SubmeshGeometry submesh;
submesh.IndexCount = (UINT)indices.size();
submesh.StartIndexLocation = 0;
submesh.BaseVertexLocation = 0;
geo->DrawArgs["grid"] = submesh;
mGeometries["landGeo"] = std::move(geo);
}
void LandAndWavesApp::BuildWavesGeometryBuffers()
{
std::vector<std::uint16_t> indices(3 * mWaves->TriangleCount()); // 3 indices per face
assert(mWaves->VertexCount() < 0x0000ffff);
// Iterate over each quad.
int m = mWaves->RowCount();
int n = mWaves->ColumnCount();
int k = 0;
for(int i = 0; i < m - 1; ++i)
{
for(int j = 0; j < n - 1; ++j)
{
indices[k] = i*n + j;
indices[k + 1] = i*n + j + 1;
indices[k + 2] = (i + 1)*n + j;
indices[k + 3] = (i + 1)*n + j;
indices[k + 4] = i*n + j + 1;
indices[k + 5] = (i + 1)*n + j + 1;
k += 6; // next quad
}
}
UINT vbByteSize = mWaves->VertexCount()*sizeof(Vertex);
UINT ibByteSize = (UINT)indices.size()*sizeof(std::uint16_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = "waterGeo";
// Set dynamically.
geo->VertexBufferCPU = nullptr;
geo->VertexBufferGPU = nullptr;
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R16_UINT;
geo->IndexBufferByteSize = ibByteSize;
SubmeshGeometry submesh;
submesh.IndexCount = (UINT)indices.size();
submesh.StartIndexLocation = 0;
submesh.BaseVertexLocation = 0;
geo->DrawArgs["grid"] = submesh;
mGeometries["waterGeo"] = std::move(geo);
}
void LandAndWavesApp::BuildPSOs()
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
//
// PSO for opaque objects.
//
ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
opaquePsoDesc.pRootSignature = mRootSignature.Get();
opaquePsoDesc.VS =
{
reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
mShaders["standardVS"]->GetBufferSize()
};
opaquePsoDesc.PS =
{
reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
mShaders["opaquePS"]->GetBufferSize()
};
opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
opaquePsoDesc.SampleMask = UINT_MAX;
opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
opaquePsoDesc.NumRenderTargets = 1;
opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
opaquePsoDesc.DSVFormat = mDepthStencilFormat;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
//
// PSO for opaque wireframe objects.
//
D3D12_GRAPHICS_PIPELINE_STATE_DESC opaqueWireframePsoDesc = opaquePsoDesc;
opaqueWireframePsoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaqueWireframePsoDesc, IID_PPV_ARGS(&mPSOs["opaque_wireframe"])));
}
void LandAndWavesApp::BuildFrameResources()
{
for(int i = 0; i < gNumFrameResources; ++i)
{
mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
1, (UINT)mAllRitems.size(), mWaves->VertexCount()));
}
}
void LandAndWavesApp::BuildRenderItems()
{
auto wavesRitem = std::make_unique<RenderItem>();
wavesRitem->World = MathHelper::Identity4x4();
wavesRitem->ObjCBIndex = 0;
wavesRitem->Geo = mGeometries["waterGeo"].get();
wavesRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
wavesRitem->IndexCount = wavesRitem->Geo->DrawArgs["grid"].IndexCount;
wavesRitem->StartIndexLocation = wavesRitem->Geo->DrawArgs["grid"].StartIndexLocation;
wavesRitem->BaseVertexLocation = wavesRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
mWavesRitem = wavesRitem.get();
mRitemLayer[(int)RenderLayer::Opaque].push_back(wavesRitem.get());
auto gridRitem = std::make_unique<RenderItem>();
gridRitem->World = MathHelper::Identity4x4();
gridRitem->ObjCBIndex = 1;
gridRitem->Geo = mGeometries["landGeo"].get();
gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
mAllRitems.push_back(std::move(wavesRitem));
mAllRitems.push_back(std::move(gridRitem));
}
void LandAndWavesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
auto objectCB = mCurrFrameResource->ObjectCB->Resource();
// For each render item...
for(size_t i = 0; i < ritems.size(); ++i)
{
auto ri = ritems[i];
cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());
cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());
cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress();
objCBAddress += ri->ObjCBIndex*objCBByteSize;
cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
}
}
float LandAndWavesApp::GetHillsHeight(float x, float z)const
{
return 0.3f*(z*sinf(0.1f*x) + x*cosf(0.1f*z));
}
XMFLOAT3 LandAndWavesApp::GetHillsNormal(float x, float z)const
{
// n = (-df/dx, 1, -df/dz)
XMFLOAT3 n(
-0.03f*z*cosf(0.1f*x) - 0.3f*cosf(0.1f*z),
1.0f,
-0.3f*sinf(0.1f*x) + 0.03f*x*sinf(0.1f*z));
XMVECTOR unitNormal = XMVector3Normalize(XMLoadFloat3(&n));
XMStoreFloat3(&n, unitNormal);
return n;
}