渲染反射折射物体

概述

来自人宅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]);
    }
}
上一篇
下一篇