概述
来自人宅Engine二期 26章的总结笔记
静态和动态CubeMap反射
- 静态的做法,视野到物体,反射到天空球采样到颜色
- 动态的做法,物体六个方向摄像机向周围采样(性能开销较大)
- 离屏渲染,渲染但是不显示,
静态反射
摄像机到物体上某个点的向量,的反射向量到天空球CubeMap上采样一个颜色,加到物体上。实现反射。这样只能反射天空,无法反射其他物体。
获取反射向量
float3 GetReflec(float3 InUnitWorldNormal, float3 WorldPosition)
{
float3 ViewDirection = normalize(ViewportPosition.xyz - WorldPosition);
return reflect(-ViewDirection, InUnitWorldNormal).xyz;
}
采样颜色
float3 GetReflectionSampleColor(float3 NewReflect)
{
return SimpleCubeMap.Sample(TextureSamplerState, NewReflect).rgb;
}
反射度
float GetShininess(MaterialConstantBuffer MatConstantBuffer)
{
return 1.f - MatConstantBuffer.MaterialRoughness; // 反射度
}
菲涅尔因子
float3 FresnelSchlickFactor(MaterialConstantBuffer MatConstantBuffer, float3 InUnitWorldNormal, float3 InReflect)
{
// 菲涅尔因子
return FresnelSchlick(MatConstantBuffer.FresnelF0, InUnitWorldNormal, InReflect, 5);
}
最后颜色是三个乘起来
float3 GetReflectionColor(MaterialConstantBuffer MatConstantBuffer, float3 InUnitWorldNormal, float3 InWorldPosition)
{
float3 InReflect = GetReflec(InUnitWorldNormal, InWorldPosition);
float3 SampleReflectionColor = GetReflectionSampleColor(InReflect);
float3 Shininess = GetShininess(MatConstantBuffer);
float3 FresnelFactor = FresnelSchlickFactor(MatConstantBuffer, InUnitWorldNormal, InReflect);
return SampleReflectionColor * Shininess * FresnelFactor;
}
动态反射
在物体表面创建六个方向的摄像机进行采样,然后对物体的渲染造成影响(性能开销较大)
首先调整了渲染流程
没有动态反射物体的渲染流程
渲染的Tick从 CDirectXRenderingEngine::Tick开始
void CDirectXRenderingEngine::Tick(float DeltaTime)
{
// reset command allocator and command list
ThrowIfFailed(CommandAllocator->Reset())
MeshMgr->PreDraw(DeltaTime);
{
StartSetMainViewportRenderTarget();
MeshMgr->Draw(DeltaTime);
MeshMgr->PostDraw(DeltaTime);
EndSetMainViewportRenderTarget();
}
// submit command list
ThrowIfFailed(GraphicsCommandList->Close())
ID3D12CommandList* CommandLists[] = {GraphicsCommandList.Get()};
CommandQueue->ExecuteCommandLists(_countof(CommandLists), CommandLists);
SwapChain->Present(0, 0);
CurrentSwapBufferIndex = (CurrentSwapBufferIndex + 1) % FEngineRenderConfig::GetRenderConfig()->SwapChainCount;
WaitGPUCommandQueueComplete();
}
掐头去尾只有这几行
MeshMgr->PreDraw(DeltaTime);
{
StartSetMainViewportRenderTarget();
MeshMgr->Draw(DeltaTime);
MeshMgr->PostDraw(DeltaTime);
EndSetMainViewportRenderTarget();
}
MeshMgr->PreDraw(DeltaTime);
void FRenderingPipeLine::PreDraw(float DeltaTime)
{
PipelineState.PreDraw(DeltaTime);
GeometryMap.PreDraw(DeltaTime);
RootSignature.PreDraw(DeltaTime);
GeometryMap.Draw(DeltaTime); // 灯光,材质,贴图等
ClearMainSwapChainCanvas();
// 动态反射
if (DynamicCubeMap.IsExitDynamicReflectionMesh())
{
DynamicCubeMap.PreDraw(DeltaTime);
}
RenderLayerMgr.PreDraw(DeltaTime);
}
- PipelineState.PreDraw(DeltaTime); Reset了PSO对象
- GeometryMap.PreDraw(DeltaTime); 初始化了描述堆,贴图和CubeMap是用描述堆传到GPU的
- RootSignature.PreDraw(DeltaTime); 设置一下根签名
- GeometryMap.Draw(DeltaTime); 绘制灯光,贴图,Cubemap天空球,材质,雾,这一步做的是这些事情
- SetGraphicsRootDescriptorTable
- SetGraphicsRootShaderResourceView
- SetGraphicsRootConstantBufferView
- ClearMainSwapChainCanvas 是清空RTV和DSV
- 如果有动态反射物体,就多一部动态Cubemap的预渲染,这一步后面细说。
- RenderLayerMgr.PreDraw(DeltaTime); 调用各个渲染层级的预渲染,翻一下目前还没有具体逻辑
StartSetMainViewportRenderTarget 和 EndSetMainViewportRenderTarget
void CDirectXRenderingEngine::StartSetMainViewportRenderTarget()
{
// transition resource state
CD3DX12_RESOURCE_BARRIER ResourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition(
GetCurrentSwapBuffer(),
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET
);
GraphicsCommandList->ResourceBarrier(1, &ResourceBarrier);
// set view port and scissor rect
GraphicsCommandList->RSSetViewports(1, &ViewportInfo);
GraphicsCommandList->RSSetScissorRects(1, &ScissorRect);
// merge render target view and depth stencil view
D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle = GetCurrentSwapBufferView();
D3D12_CPU_DESCRIPTOR_HANDLE DSVHandle = GetCurrentDepthStencilView();
GraphicsCommandList->OMSetRenderTargets(1, &RTVHandle, true, &DSVHandle);
}
void CDirectXRenderingEngine::EndSetMainViewportRenderTarget()
{
// finish
CD3DX12_RESOURCE_BARRIER Barrier = CD3DX12_RESOURCE_BARRIER::Transition(
GetCurrentSwapBuffer(),
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT
);
GraphicsCommandList->ResourceBarrier(1, &Barrier);
}
- Start 和 End 包起来的代码块 Draw 和 PostDraw (PostDraw现在啥也没有)
- 设置 Barrier 状态
- 设置视口和裁切矩形
- 设置RTV, DSV 到输出合并阶段
MeshMgr->Draw(DeltaTime);
直接走到rendering pipeline 的 Draw
void FRenderingPipeLine::Draw(float DeltaTime)
{
GeometryMap.DrawViewport(DeltaTime); // main viewport
GeometryMap.DrawCubeMapTexture(DeltaTime);
RenderLayerMgr.Draw(EMeshRenderingType::eBackground, DeltaTime);
RenderLayerMgr.Draw(EMeshRenderingType::eOpaque, DeltaTime);
RenderLayerMgr.Draw(EMeshRenderingType::eTransparent, DeltaTime);
PipelineState.Draw(DeltaTime);
}
- GeometryMap.DrawViewport(DeltaTime); 通过 SetGraphicsRootConstantBufferView 设置视口的常量缓冲区,拿来传递view 和 project 矩阵到GPU
- GeometryMap.DrawCubeMapTexture(DeltaTime); 后面细说
- 然后按顺序渲染,背景层,不透明层,透明层。并调用每一个渲染层的DrawObject函数
void FRenderLayer::DrawObject(float DeltaTime, const FRenderingData& InRenderingData)
{
// const UINT DescriptorOffset = GetD3dDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
UINT MeshOffset = GeometryMap->MeshConstantBufferView.GetConstantBufferByteSize();
D3D12_VERTEX_BUFFER_VIEW VBV = GeometryMap->Geometries[InRenderingData.GeometryKey].GetVertexBufferView();
D3D12_INDEX_BUFFER_VIEW IBV = GeometryMap->Geometries[InRenderingData.GeometryKey].GetIndexBufferView();
// auto DesMeshHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(GeometryMap->GetDescriptorHeap()->GetGPUDescriptorHandleForHeapStart());
auto VirtualMeshAddrStart = GeometryMap->MeshConstantBufferView.GetUploadBuffer()->GetGPUVirtualAddress();
GetD3dGraphicsCommandList()->IASetIndexBuffer(&IBV);
GetD3dGraphicsCommandList()->IASetVertexBuffers(
0, // StartSlot 0 ~ 15 输入槽
1, // k, k + 1, k + 2, k + n - 1 顶点缓冲区的数量
&VBV);
if (InRenderingData.Mesh->GetMaterials()->size() > 0)
{
const CMaterial* Material = (*InRenderingData.Mesh->GetMaterials())[0];
EMaterialDisplayStatusType MaterialDisplayStatus = Material->GetMaterialDisplayStatus();
GetD3dGraphicsCommandList()->IASetPrimitiveTopology(static_cast<D3D12_PRIMITIVE_TOPOLOGY>(MaterialDisplayStatus));
}
else
{
GetD3dGraphicsCommandList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
// mesh offset
// DesMeshHandle.Offset(InRenderingData.MeshObjectIndex, DescriptorOffset);
// GetD3dGraphicsCommandList()->SetGraphicsRootDescriptorTable(0, DesMeshHandle);
auto VirtualAddr = VirtualMeshAddrStart + InRenderingData.MeshObjectIndex * MeshOffset;
GetD3dGraphicsCommandList()->SetGraphicsRootConstantBufferView(0, VirtualAddr);
GetD3dGraphicsCommandList()->DrawIndexedInstanced(
InRenderingData.IndexSize, // 顶点数量
1, // 绘制实例数量
InRenderingData.IndexOffsetPosition, // 顶点缓冲区第一个被绘制的索引
InRenderingData.VertexOffsetPosition, // GPU 从索引缓冲区读取的第一个索引的位置
0 // 从顶点缓冲区读取每个实例数据之前添加到每个索引的值
);
}
- 拿到本渲染层应该渲染的物体的顶点和索引列表 IASetIndexBuffer,IASetVertexBuffers
- IASetPrimitiveTopology 图元拓扑类型
- 绘制,SetGraphicsRootConstantBufferView,0 是每个物体用的常量缓冲区
cbuffer MeshConstantBuffer : register(b0) // b0~b14
{
float4x4 WorldMatrix;
float4x4 ObjectTextureTransform;
uint MaterialIndex;
uint _MPadding1; // 对齐
uint _MPadding2; // 对齐
uint _MPadding3; // 对齐
};
- DrawIndexedInstanced
- PipelineState.Draw(DeltaTime); 写了个输入捕获,没有特殊逻辑
有反射物体的渲染流程
// 反射测试球
// 需要 SetMeshRenderingType(eOpaqueReflect) 并且 SetDynamicReflection(true)
if (auto* Sphere = World->CreateActorObject<GSphereMesh>())
{
Sphere->SetMeshRenderingType(eOpaqueReflect);
Sphere->CreateMesh(1.f, 20.f, 30.f);
Sphere->SetPosition(XMFLOAT3(6, 4, 0));
if (CMaterial* Material = Sphere->GetMaterials()->at(0))
{
Material->SetDynamicReflection(true);
Material->SetMaterialType(BlinnPhong);
// Material->SetBaseColor("Content/Tex/Earth.dds");
Material->SetSpecular(FVector3D(1.f, 1.f, 1.f));
Material->SetFresnelF0(FVector3D(0.07f, 0.07f, 0.07f));
Material->SetMaterialRoughness(0.1f);
}
}
- 首先创建一个动态反射球,需要让这个物体在不透明反射层被渲染 Sphere->SetMeshRenderingType(eOpaqueReflect); 并且标记为动态反射 Material->SetDynamicReflection(true);
- 被标记为 SetDynamicReflection 的物体会被丢到一个vector里面,CubeMapRenderTarget在更新的时候只会处理这部分物体
- 渲染分层,每一个RenderLayer对象,有自己的PSO,自己的一个vector存属于这个渲染层的物体数据。
- 目前有这些渲染层
- 背景层用于渲染天空球
- 不透明层渲染普通物体
- 透明层渲染物体
- 不透明物体反射层渲染我们这里的动态反射物体
- 我们需要按顺序渲染:
- 循环渲染对于每一个反射球【背景层->不透明层->透明层->不透明物体反色层(只渲染一个反射球)】
- 继续渲染【背景层->不透明层->透明层】覆盖掉上面的,给主视口(因为上面每一次渲染,都是渲染到天空CubeMap,然后在给反射物体读的,所以不重新渲染一边,天空上就会有好多球了)
这一块就是上面的
// 动态反射
if (DynamicCubeMap.IsExitDynamicReflectionMesh())
{
DynamicCubeMap.PreDraw(DeltaTime);
}
具体代码:
void FDynamicCubeMap::PreDraw(float DeltaTime)
{
for (size_t j = 0; j < GeometryMap->DynamicReflectionMeshes.size(); j++)
{
// 。。。
// Barrier,viewport, ScissorRect
UINT CBVSize = GeometryMap->ViewportConstantBufferView.GetConstantBufferByteSize();
for (size_t i = 0; i < CUBE_MAP_VIEWPORT_NUM; i++)
{
// ... clear RTV, DSV and OMSetRenderTargets
auto ViewportAddr = GeometryMap->ViewportConstantBufferView.GetUploadBuffer()->GetGPUVirtualAddress();
ViewportAddr += (1 + i + j * CUBE_MAP_VIEWPORT_NUM) * CBVSize; // MainViewport:1 + CubeMapViewport:6 for each mesh
GetD3dGraphicsCommandList()->SetGraphicsRootConstantBufferView(1, ViewportAddr); // 1 root parameter view port
// RenderLayerMgr->Draw(DeltaTime);
RenderLayerMgr->Draw(EMeshRenderingType::eBackground, DeltaTime);
RenderLayerMgr->Draw(EMeshRenderingType::eOpaque, DeltaTime);
RenderLayerMgr->Draw(EMeshRenderingType::eTransparent, DeltaTime);
}
// 。。。
/// Barrier
{
StartSetMainViewportRenderTarget();
GeometryMap->DrawViewport(DeltaTime);
// 画cube map
Draw(DeltaTime);
// 对某个模型进行渲染
RenderLayerMgr->FindObjectDraw(DeltaTime, EMeshRenderingType::eOpaqueReflect, GeometryMap->DynamicReflectionMeshes[j]);
// 重置cube map
GeometryMap->DrawCubeMapTexture(DeltaTime);
EndSetMainViewportRenderTarget();
}
}
}
- 遍历 DynamicReflectionMeshes。这个就是一开始我们标记动态反射物体的 SetDynamicReflection
- 第一部分:
UINT CBVSize = GeometryMap->ViewportConstantBufferView.GetConstantBufferByteSize();
for (size_t i = 0; i < CUBE_MAP_VIEWPORT_NUM; i++)
{
// ... clear RTV, DSV and OMSetRenderTargets
auto ViewportAddr = GeometryMap->ViewportConstantBufferView.GetUploadBuffer()->GetGPUVirtualAddress();
ViewportAddr += (1 + i + j * CUBE_MAP_VIEWPORT_NUM) * CBVSize; // MainViewport:1 + CubeMapViewport:6 for each mesh
GetD3dGraphicsCommandList()->SetGraphicsRootConstantBufferView(1, ViewportAddr); // 1 root parameter view port
RenderLayerMgr->Draw(EMeshRenderingType::eBackground, DeltaTime);
RenderLayerMgr->Draw(EMeshRenderingType::eOpaque, DeltaTime);
RenderLayerMgr->Draw(EMeshRenderingType::eTransparent, DeltaTime);
}
- 对于每一个动态反射球,他有六个摄像机。前面我们构建好了,现在 ViewportAddr 就是对应偏移。
- 在用这个摄像机的情况下,渲染背景,不透明层,透明层。画了六次
- 第二部分:
{
StartSetMainViewportRenderTarget();
GeometryMap->DrawViewport(DeltaTime);
// 画cube map
Draw(DeltaTime);
// 对某个模型进行渲染
RenderLayerMgr->FindObjectDraw(DeltaTime, EMeshRenderingType::eOpaqueReflect, GeometryMap->DynamicReflectionMeshes[j]);
// 重置cube map
GeometryMap->DrawCubeMapTexture(DeltaTime);
EndSetMainViewportRenderTarget();
}
- 设置摄像机为主视口
- Draw(DeltaTime); 画CubeMap。实际调用GetD3dGraphicsCommandList()->SetGraphicsRootDescriptorTable(6, RenderTarget->GPUShaderResourceView); 把 RenderTarget 渲染在天空球上
- 渲染动态反射物体,由于这个物体我们设置了反射,shader那边就会把画好动态物体的天空球,写到物体上,实现动态反射
- 重置CubeMap为原来的状态。
总结全部流程:物体六个摄像机,的视角看到的存到RT上,然后渲染到天空CubeMap, 在绘制到物体上。
摄像机的调整 GClientViewport
六个摄像机类为GClientViewport。子类CCamera为主摄像机
class GClientViewport : public GActorObject, public FViewport
{
using SuperA = GActorObject;
using SuperV = FViewport;
public:
GClientViewport();
void SetFrustum(float InFov, float InAspect, float InNear, float InFar);
bool FaceTarget(const FVector3D& InPosition, const FVector3D& InTarget, const FVector3D& InUp = FVector3D(0.0f, 1.0f, 0.0f));
void Tick(float DeltaTime) override;
virtual void BuildViewMatrix(float DeltaTime);
// ...
private:
float Fov;
float Aspect;
float Near;
float Far;
bool bDirty;
};
SetFrustum
设置视锥体,目的是构造project矩阵
void GClientViewport::SetFrustum(float InFov, float InAspect, float InNear, float InFar)
{
Fov = InFov;
Aspect = InAspect;
Near = InNear;
Far = InFar;
auto Project = XMMatrixPerspectiveFovLH(
Fov, // Field of View Angle
Aspect, // Aspect ratio
Near, // Near clip plane distance
Far); // Far clip plane distance
XMStoreFloat4x4(&ProjectionMatrix, Project);
SetDirty(true);
}
FaceTarget
摄像机位于Pos,上方向是Up向量,朝向目标Target。
用已知的前向量(Target向量) 和 上向量。求出右向量。
物体坐标,和物体的前,上,右方向都确定。我们就能确定物体在空间中的状态。
bool GClientViewport::FaceTarget(const FVector3D& InPosition, const FVector3D& InTarget, const FVector3D& InUp)
{
FVector3D TmpFaceVector = InTarget - InPosition;
TmpFaceVector.Normalize();
FVector3D TmpRight = FVector3D::CrossProduct(InUp, TmpFaceVector);
TmpRight.Normalize();
FVector3D TmpUp = FVector3D::CrossProduct(TmpFaceVector, TmpRight);
TmpUp.Normalize();
XMFLOAT3 XMFaceVector = EngineMath::ToFloat3(TmpFaceVector);
XMFLOAT3 XMRight = EngineMath::ToFloat3(TmpRight);
XMFLOAT3 XMUp = EngineMath::ToFloat3(TmpUp);
SetForwardVector(XMFaceVector);
SetRightVector(XMRight);
SetUpVector(XMUp);
SetDirty(true);
return true;
}
BuildViewMatrix
然后就可以算出View矩阵
void GClientViewport::BuildViewMatrix(float DeltaTime)
{
// 计算和矫正轴
GetTransformationComponent()->CorrectionVector();
// 算出按自身方向移动的意图
float X = 0, Y = 0, Z = 0;
GetTransformationComponent()->GetCorrectionPosition(X, Y, Z);
// 构建View
XMFLOAT3 RightVector = GetTransformationComponent()->GetRightVector();
XMFLOAT3 UpVector = GetTransformationComponent()->GetUpVector();
XMFLOAT3 ForwardVector = GetTransformationComponent()->GetForwardVector();
ViewMatrix =
{
RightVector.x, UpVector.x, ForwardVector.x, 0.0f,
RightVector.y, UpVector.y, ForwardVector.y, 0.0f,
RightVector.z, UpVector.z, ForwardVector.z, 0.0f,
X, Y, Z, 1.0f
};
}
- GetTransformationComponent()->CorrectionVector(); 确保Forward, Up, Right 正交
void CTransformationComponent::CorrectionVector()
{
XMVECTOR Right = XMLoadFloat3(&RightVector);
XMVECTOR Up = XMLoadFloat3(&UpVector);
XMVECTOR Forward = XMLoadFloat3(&ForwardVector);
Forward = XMVector3Normalize(Forward);
Up = XMVector3Normalize(XMVector3Cross(Forward, Right));
Right = XMVector3Cross(Up, Forward);
XMStoreFloat3(&RightVector, Right);
XMStoreFloat3(&UpVector, Up);
XMStoreFloat3(&ForwardVector, Forward);
}
- GetTransformationComponent()->GetCorrectionPosition(X, Y, Z);
void CTransformationComponent::GetCorrectionPosition(float& X, float& Y, float& Z)
{
XMVECTOR Right = XMLoadFloat3(&RightVector);
XMVECTOR Up = XMLoadFloat3(&UpVector);
XMVECTOR Forward = XMLoadFloat3(&ForwardVector);
XMVECTOR PositionVector = XMLoadFloat3(&Position);
X = -XMVectorGetX(XMVector3Dot(PositionVector, Right));
Y = -XMVectorGetX(XMVector3Dot(PositionVector, Up));
Z = -XMVectorGetX(XMVector3Dot(PositionVector, Forward));
}
- X = -XMVectorGetX(XMVector3Dot(PositionVector, Right));
- 点乘获得的是,PositionVector 在 Right 方向上的投影的长度
- 这个长度总是非负的。因此,我们需要通过取反操作,使得当物体向左移动时,得到的X坐标是正数,这样才能正确表示物体在自身坐标系下的位置。
六个摄像机
每一个RT有一个vector,用于存这六个摄像机的信息
std::vector<GClientViewport*> CubeMapViewportList;
构建,简单来说就是设置一下摄像机的朝向
void FDynamicCubeMap::BuildViewport(const FVector3D& InCenterPoint)
{
const FTmpViewportCapture& TmpViewportCapture = BuildViewportCapture(InCenterPoint);
for (size_t i = 0; i < CUBE_MAP_VIEWPORT_NUM; ++i)
{
auto TmpViewport = CreateObject<GClientViewport>(new GClientViewport());
TmpViewport->SetPosition(EngineMath::ToFloat3(InCenterPoint));
TmpViewport->FaceTarget(InCenterPoint, TmpViewportCapture.TargetPoint[i], TmpViewportCapture.Up[i]);
TmpViewport->SetFrustum(0.5f * XM_PI, 1.0f, 0.1f, 10000.0f);
TmpViewport->BuildViewMatrix(0.3f); // TODO: DeltaTime
CubeMapViewportList.push_back(TmpViewport);
}
}
折射
TODO 整理一下光照代码再回来写
RenderTarget 初始化流程
这个就是上述我们把六个摄像机的东西画在RenderTarget上的那个对象。本质是一个 ID3D12Resource 的包装
class FCubeMapRenderTarget
: public IDirectXDeviceInterface,
std::enable_shared_from_this<FCubeMapRenderTarget>
{
public:
// ...
private:
UINT Width;
UINT Height;
DXGI_FORMAT Format;
D3D12_VIEWPORT Viewport;
D3D12_RECT ScissorRect;
ComPtr<ID3D12Resource> RenderTargetMap;
// SRV
CD3DX12_CPU_DESCRIPTOR_HANDLE CPUShaderResourceView;
CD3DX12_GPU_DESCRIPTOR_HANDLE GPUShaderResourceView;
// RTV
std::vector<CD3DX12_CPU_DESCRIPTOR_HANDLE> CPURenderTargetView;
};
我们需要一个渲染的宽,高,资源格式,Viewport描述,裁剪描述ScissorRect,一个资源ID3D12Resource 对象的RT。还有SRV指针指向我们的DynamicCubeMap
RT初始化的时候干了这些事情
void FRenderingPipeLine::BuildPipeline()
{
// ...
DynamicCubeMap.Init(&GeometryMap, &PipelineState, &RenderLayerMgr);
DynamicCubeMap.BuildDepthStencilDesc();
DynamicCubeMap.BuildDepthStencil();
// ...
// 可以随便设置一个位置,因为在UpdateCalculations中会更新位置
DynamicCubeMap.BuildViewport(FVector3D(0, 0, 0));
// 构建CubeMap
DynamicCubeMap.BuildRenderTargetDesc();
// ...
}
我们回忆一下我们绘制窗口的时候,交换链的ID3D12Resource需要啥
RTV 和 DSV。
DSV就是上面 DynamicCubeMap.BuildDepthStencilDesc(); DynamicCubeMap.BuildDepthStencil();、
RTV就是 DynamicCubeMap.BuildRenderTargetDesc(); -> BuildRenderTargetRTV();
DynamicCubeMap.BuildRenderTargetDesc()->BuildRenderTargetSRV() 创建SRV
RenderTarget->Init(Width, Height, DXGI_FORMAT_R8G8B8A8_UNORM); 视口和裁切矩形
然后三个build
BuildRenderTargetMap();
BuildRTVDescriptors();
BuildSRVDescriptors();
具体一点
创建深度模板描述,然后创建深度模板
DynamicCubeMap.BuildDepthStencilDesc();
DynamicCubeMap.BuildDepthStencil();
最后获得了一个 ID3D12Resource 的深度模板buff
- 创建RenderTarget
看BuildRenderTargetDesc
void FDynamicCubeMap::BuildRenderTargetDesc()
{
BuildRenderTargetRTV();
BuildRenderTargetSRV();
RenderTarget->Init(Width, Height, DXGI_FORMAT_R8G8B8A8_UNORM);
}
- BuildRenderTargetRTV
void FDynamicCubeMap::BuildRenderTargetRTV()
{
FEngineRenderConfig* RenderConfig = FEngineRenderConfig::GetRenderConfig();
UINT RTVDescSize = GetDescriptorHandleIncrementSizeRTV();
auto RTVDescAddr = GetRtvHeap()->GetCPUDescriptorHandleForHeapStart();
for (size_t i = 0; i < 6; i++)
{
RenderTarget->CPURenderTargetView[i] = CD3DX12_CPU_DESCRIPTOR_HANDLE(
RTVDescAddr,
RenderConfig->SwapChainCount + i,
RTVDescSize);
}
}
我们主流程创建了RTV,本来是2个,是给交换链用的,我们多创建6个。现在把RT用的着六个指针指过去。(交换链渲染了但是不参与多重缓冲绘制到屏幕上,而用于其他地方->离屏渲染)
SRV
void FDynamicCubeMap::BuildRenderTargetSRV()
{
auto CPUStart = GeometryMap->GetDescriptorHeap()->GetCPUDescriptorHandleForHeapStart();
auto GPUStart = GeometryMap->GetDescriptorHeap()->GetGPUDescriptorHandleForHeapStart();
auto CBVDescSize = GetDescriptorHandleIncrementSizeCBV_SRV_UAV();
RenderTarget->CPUShaderResourceView = CD3DX12_CPU_DESCRIPTOR_HANDLE(
CPUStart,
GeometryMap->GetDrawTextureNumber() + GeometryMap->GetCubeMapNumber(),
CBVDescSize);
RenderTarget->GPUShaderResourceView = CD3DX12_GPU_DESCRIPTOR_HANDLE(
GPUStart,
GeometryMap->GetDrawTextureNumber() + GeometryMap->GetCubeMapNumber(),
CBVDescSize);
}
前面我们创建描述堆的时候
void FGeometryMap::BuildDescriptorHeap()
{
DescriptorHeap.BuildDescriptorHeap(
0 + // 排版好看
GetDrawTextureNumber() + // Texture
GetCubeMapNumber() + // Static CubeMap like Sky
1); // DynamicCubeMap
}
这里 GeometryMap->GetDrawTextureNumber() + GeometryMap->GetCubeMapNumber() 是把 SRV指针指向我们的动态CubeMap
我们最终目标是要构建这个 ID3D12Resource,所以需要 CreateCommittedResource
void FCubeMapRenderTarget::BuildRenderTargetMap()
{
D3D12_RESOURCE_DESC RenderTargetMapDesc = {};
RenderTargetMapDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
RenderTargetMapDesc.Alignment = 0;
RenderTargetMapDesc.Width = Width;
RenderTargetMapDesc.Height = Height;
RenderTargetMapDesc.DepthOrArraySize = 6;
RenderTargetMapDesc.MipLevels = 1;
RenderTargetMapDesc.Format = Format;
RenderTargetMapDesc.SampleDesc.Count = 1;
RenderTargetMapDesc.SampleDesc.Quality = 0;
RenderTargetMapDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
RenderTargetMapDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
// D3D12_CLEAR_VALUE RenderTargetMapClearValue = {};
// RenderTargetMapClearValue.Format = Format;
// RenderTargetMapClearValue.Color[0] = 0.0f;
// RenderTargetMapClearValue.Color[1] = 0.0f;
// RenderTargetMapClearValue.Color[2] = 0.0f;
// RenderTargetMapClearValue.Color[3] = 1.0f;
auto Properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
ThrowIfFailed(GetD3dDevice()->CreateCommittedResource(
&Properties,
D3D12_HEAP_FLAG_NONE,
&RenderTargetMapDesc,
D3D12_RESOURCE_STATE_RENDER_TARGET,
nullptr,
IID_PPV_ARGS(&RenderTargetMap)
));
}
SRV创建
void FCubeMapRenderTarget::BuildSRVDescriptors()
{
D3D12_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
SRVDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
SRVDesc.Format = Format;
SRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
SRVDesc.TextureCube.MostDetailedMip = 0;
SRVDesc.TextureCube.MipLevels = 1;
SRVDesc.TextureCube.ResourceMinLODClamp = 0.f;
GetD3dDevice()->CreateShaderResourceView(RenderTargetMap.Get(), &SRVDesc, CPUShaderResourceView);
}
RTV创建
void FCubeMapRenderTarget::BuildRTVDescriptors()
{
for (size_t i = 0; i < 6; i++)
{
D3D12_RENDER_TARGET_VIEW_DESC RTVDesc = {};
RTVDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
RTVDesc.Format = Format;
// MipSlice + ArraySlice * MipLevels ??? 26-9
RTVDesc.Texture2DArray.MipSlice = 0;
RTVDesc.Texture2DArray.PlaneSlice = 0;
RTVDesc.Texture2DArray.FirstArraySlice = i;
RTVDesc.Texture2DArray.ArraySize = 1; // only one texture for each RTV
GetD3dDevice()->CreateRenderTargetView(RenderTargetMap.Get(), &RTVDesc, CPURenderTargetView[i]);
}
}