雾的实现

概述

来自人宅DX12引擎第二期,24章雾实现的笔记。https://renzhai.net/
https://www.yuque.com/chenweilin-tryw7/gb93ve/xn5hu25quu24q73x?singleDoc# 《雾的实现》
大致原理,当前位置画一个圈。半径内物体的颜色都是原来的颜色。
半径外,物体颜色和灰色做一个差值,越远越灰,就形成了雾的效果。

雾的对象组件设计

创建雾对象,GFog类基本是空的,中转调用组件,主要逻辑在雾气组件中

    if (GFog* Fog = World->CreateActorObject<GFog>())
    {
        Fog->SetFogColor(FColor(0.5f, 0.5f, 0.5f, 1.f));
        Fog->SetFogStart(10.f);
        Fog->SetFogRange(100.f);
    }

组件提供雾颜色,开始位置,范围,高度等参数。还有基本的set,get。

class CFogComponent : public CComponent
{
    using Super = CComponent;

    // ...
protected:
    FColor FogColor;
    float FogStart;
    float FogRange;
    float FogHeight;
    bool bDirty;
};

雾的常量缓冲区

首先是根签名新增雾的部分,在3

void FDirectXRootSignature::BuildRootSignature(UINT InTextureNumber)
{
    constexpr int CountBuffer = 7;

    // root signature
    CD3DX12_ROOT_PARAMETER RootParameters[CountBuffer];

    // SRV
    // ...

    RootParameters[0].InitAsConstantBufferView(0); // OBJ
    RootParameters[1].InitAsConstantBufferView(1); // ViewPort
    RootParameters[2].InitAsConstantBufferView(2); // Light
    RootParameters[3].InitAsConstantBufferView(3); // Fog

    RootParameters[4].InitAsShaderResourceView(0, 1); // Material
    RootParameters[5].InitAsDescriptorTable(1, &DescriptorRangeTextureSRV, D3D12_SHADER_VISIBILITY_PIXEL); // Texture
    RootParameters[6].InitAsDescriptorTable(1, &DescriptorRangeCubeMapSRV, D3D12_SHADER_VISIBILITY_PIXEL); // CubeMap

    // ...
}

注意,由于我们Fog是中途插进来的3。后面4,5,6向后挪了,SetGraphicsRootConstantBufferView 用到根签名的地方需要调整

常量缓冲区结构

struct FFogConstantBuffer
{
    FFogConstantBuffer();

    XMFLOAT3 Color;
    float FogStart;

    float FogRange;
    float FogHeight;
    float Padding[2];
};

FGeometryMap 新增

FConstantBufferView FogConstantBufferView;

构建常量缓冲区,暂时就一个

void FGeometryMap::BuildFogConstantBuffer()
{
    FogConstantBufferView.CreateConstantBufferView(sizeof(FFogConstantBuffer), 1);
}

在RenderingPipeLine 里面调用一下

void FRenderingPipeLine::BuildPipeline()
{
    // 初始化GPS描述
    PipelineState.ResetGPSDesc();

    // 初始化渲染层级
    RenderLayerMgr.Init(&GeometryMap, &PipelineState);

    // 渲染层级排序
    RenderLayerMgr.SortRenderLayers();

    // 读取贴图
    GeometryMap.LoadTexture();

    // 构建雾气 《----------------------------------------------------------------------------- new
    GeometryMap.BuildFog();

    // 构建根签名
    RootSignature.BuildRootSignature(GeometryMap.GetDrawTextureNumber());
    PipelineState.BindRootSignature(RootSignature.GetRootSignature());

    // 构建模型
    GeometryMap.Build();

    // 构建常量描述符堆
    GeometryMap.BuildDescriptorHeap();

    // 构建Mesh常量缓冲区
    GeometryMap.BuildMeshConstantBuffer();

    // 构建材质常量缓冲区
    GeometryMap.BuildMaterialShaderResourceView();

    // 构建光照常量缓冲区
    GeometryMap.BuildLightConstantBuffer();

    // 构建视口常量缓冲区
    GeometryMap.BuildViewportConstantBufferView();

    // 构建纹理常量缓冲区
    GeometryMap.BuildTextureConstantBuffer();

    // 构建雾常量缓冲区 《----------------------------------------------------------------------------- new
    GeometryMap.BuildFogConstantBuffer();

    // 分层构建PSO
    RenderLayerMgr.BuildPSO();
}

在ShaderCommon.hlsl定义雾气的常量缓冲区

cbuffer FogConstantBuffer : register(b3)
{
    float3 FogColor;
    float FogStart;

    float FogRange;
    float FogHeight;
    float FogPadding1;
    float FogPadding2;
};

绘制中添加 DrawFog

void FGeometryMap::Draw(float DeltaTime)
{
    DrawViewport(DeltaTime);
    DrawLight(DeltaTime);
    DrawTexture(DeltaTime);
    DrawMaterial(DeltaTime);
    DrawFog(DeltaTime);
}

绘制

void FGeometryMap::DrawFog(float DeltaTime)
{
    GetD3dGraphicsCommandList()->SetGraphicsRootShaderResourceView(3, FogConstantBufferView.GetUploadBuffer()->GetGPUVirtualAddress());
}

在FGeometryMap里面新增一个雾的组件 CFogComponent* Fog; 并在构造函数制空。如果用户设置了雾。就更新常量缓冲区
根据对象是否空判断是否启动雾气

void FGeometryMap::UpdateCalculations(float DeltaTime, const FViewportInfo& ViewportInfo)
{
    // ...

    // Fog
    if (Fog)
    {
        FFogConstantBuffer FogConstantBuffer;
        {
            FColor FogColor = Fog->GetFogColor();
            FogConstantBuffer.Color = XMFLOAT3(FogColor.R, FogColor.G, FogColor.B);
            FogConstantBuffer.FogStart = Fog->GetFogStart();
            FogConstantBuffer.FogRange = Fog->GetFogRange();
            FogConstantBuffer.FogHeight = Fog->GetFogHeight();
        }
        FogConstantBufferView.Update(0, &FogConstantBuffer);
    }
}

构建雾, 从全局对象里面找到雾的对象。如果找到了,更新的时候就会把CPU的值更新到GPU去。后面我们在shader里面写

void FGeometryMap::BuildFog()
{
    for (auto& Obj : GObjects)
    {
        if (auto InFogComponent = dynamic_cast<CFogComponent*>(Obj))
        {
            Fog = InFogComponent;
            break;
        }
    }
}

雾的算法

在像素着色器的最后,在颜色输出前,最后做一次雾的计算即可。

float4 PSMain(MeshVertexOut MVOut) : SV_TARGET
{
    // ...

    MVOut.Color = GetFogValue(MVOut.Color, MVOut.WorldPosition.xyz);

    return MVOut.Color;
}

差不多是这样,有雾计算,没雾直接返回

#include "ShaderCommon.hlsl"

float4 GetFogValue(float4 InColor, float3 InPointPositon)
{
#if START_UP_FOG    
    float Distance = length(InPointPositon - ViewportPosition.xyz);
    float AlphaValue = saturate((Distance - FogStart) / FogRange);
    InColor.xyz = lerp(InColor.xyz, FogColor, AlphaValue);
#endif
    return InColor;
}

很好理解,距离远颜色就越接近雾的颜色,否则就是材质本身的颜色

远一点会有灰蒙蒙的效果

image.png

雾的高度

仔细想想,大雾天,我们是能看到远处很高的高楼的。大概这种感觉
对高度在做一次lerp

float4 GetFogValue(float4 InColor, float3 InPointPositon)
{
#if START_UP_FOG    
    float Distance = length(InPointPositon - ViewportPosition.xyz);
    float AlphaValue = saturate((Distance - FogStart) / FogRange);
    float3 Color = lerp(InColor.xyz, FogColor, AlphaValue);
    float HeightAlphaValue = saturate((InPointPositon.y - ViewportPosition.y) / max(FogHeight, 0.1f));
    InColor.xyz = lerp(Color, InColor.xyz, HeightAlphaValue);
#endif
    return InColor;
}

雾的通透感

远的物体就完全没了,一片灰
通透感变量,体现在这里

InColor.xyz = lerp(Color, InColor.xyz, HeightAlphaValue - FogTransitionCoefficient);
上一篇
下一篇