Add Volumetric Fog support for particles to modify local fog

This commit is contained in:
Wojtek Figat
2021-03-05 13:56:07 +01:00
parent dfb502621d
commit 1dee615d6e
22 changed files with 726 additions and 84 deletions

View File

@@ -218,6 +218,13 @@ struct DrawCall
uint32 SegmentCount;
GPUBuffer* SegmentDistances;
} Ribbon;
struct
{
Vector3 Position;
float Radius;
int32 ParticleIndex;
} VolumetricFog;
} Particle;
struct

View File

@@ -350,6 +350,7 @@ void RenderList::Clear()
DirectionalLights.Clear();
EnvironmentProbes.Clear();
Decals.Clear();
VolumetricFogParticles.Clear();
Sky = nullptr;
AtmosphericFog = nullptr;
Fog = nullptr;

View File

@@ -303,6 +303,11 @@ public:
/// </summary>
Array<Decal*> Decals;
/// <summary>
/// Local volumetric fog particles registered for the rendering.
/// </summary>
Array<DrawCall> VolumetricFogParticles;
/// <summary>
/// Sky/skybox renderer proxy to use (only one per frame)
/// </summary>

View File

@@ -253,13 +253,9 @@ GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext
if (renderContext.Buffers->LocalShadowedLightScattering == nullptr)
{
ASSERT(renderContext.Buffers->LastFrameVolumetricFog == Engine::FrameCount);
const GPUTextureDescription volumeDescRGB = GPUTextureDescription::New3D(_cache.GridSize, PixelFormat::R11G11B10_Float, GPUTextureFlags::RenderTarget | GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess);
const auto texture = RenderTargetPool::Get(volumeDescRGB);
renderContext.Buffers->LocalShadowedLightScattering = texture;
context->Clear(texture->ViewVolume(), Color::Transparent);
}
@@ -299,7 +295,7 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
// Bind the output
context->SetRenderTarget(localShadowedLightScattering);
context->SetViewportAndScissors((float)_cache.Data.GridSizeIntX, (float)_cache.Data.GridSizeIntY);
context->SetViewportAndScissors(_cache.Data.GridSize.X, _cache.Data.GridSize.Y);
// Setup data
perLight.MinZ = volumeZBoundsMin;
@@ -481,8 +477,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
&& Vector3::NearEqual(renderContext.Buffers->VolumetricFogHistory->Size3(), cache.GridSize);
// Allocate buffers
const GPUTextureDescription volumeDesc = GPUTextureDescription::New3D(cache.GridSize, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess);
const GPUTextureDescription volumeDescRGB = GPUTextureDescription::New3D(cache.GridSize, PixelFormat::R11G11B10_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess);
const GPUTextureDescription volumeDesc = GPUTextureDescription::New3D(cache.GridSize, PixelFormat::R16G16B16A16_Float, GPUTextureFlags::RenderTarget | GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess);
const GPUTextureDescription volumeDescRGB = GPUTextureDescription::New3D(cache.GridSize, PixelFormat::R11G11B10_Float, GPUTextureFlags::RenderTarget | GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess);
auto vBufferA = RenderTargetPool::Get(volumeDesc);
auto vBufferB = RenderTargetPool::Get(volumeDescRGB);
const auto lightScattering = RenderTargetPool::Get(volumeDesc);
@@ -500,15 +496,78 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
context->BindUA(1, vBufferB->ViewVolume());
context->Dispatch(_csInitialize, groupCountX, groupCountY, groupCountZ);
context->UnBindUA(0);
context->UnBindUA(1);
context->FlushState();
}
// Voxelize local fog particles
// TODO: support rendering volume particles with custom Albedo/Extinction/Emissive
// Render local fog particles
if (renderContext.List->VolumetricFogParticles.HasItems())
{
PROFILE_GPU_CPU("Local Fog");
// Unbind fog attributes tables
context->UnBindUA(0);
context->UnBindUA(1);
context->FlushState();
// Bind the output
GPUTextureView* rt[] = { vBufferA->ViewVolume(), vBufferB->ViewVolume() };
context->SetRenderTarget(nullptr, Span<GPUTextureView*>(rt, 2));
context->SetViewportAndScissors((float)volumeDesc.Width, (float)volumeDesc.Height);
// Ensure to have valid buffers created
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
InitCircleBuffer();
MaterialBase::BindParameters bindParams(context, renderContext);
bindParams.DrawCallsCount = 1;
CustomData customData;
customData.Shader = _shader->GetShader();
customData.GridSize = cache.GridSize;
customData.VolumetricFogMaxDistance = cache.Data.VolumetricFogMaxDistance;
bindParams.CustomData = &customData;
for (auto& drawCall : renderContext.List->VolumetricFogParticles)
{
const BoundingSphere bounds(drawCall.Particle.VolumetricFog.Position, drawCall.Particle.VolumetricFog.Radius);
ASSERT(!bounds.Center.IsNanOrInfinity() && !isnan(bounds.Radius) && !isinf(bounds.Radius));
// Calculate light volume bounds in camera frustum depth range (min and max)
const Vector3 viewSpaceLightBoundsOrigin = Vector3::Transform(bounds.Center, view.View);
const float furthestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z + bounds.Radius, options, cache.GridSizeZ);
const float closestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z - bounds.Radius, options, cache.GridSizeZ);
const int32 volumeZBoundsMin = (int32)Math::Clamp(closestSliceIndexUnclamped, 0.0f, cache.GridSize.Z - 1.0f);
const int32 volumeZBoundsMax = (int32)Math::Clamp(furthestSliceIndexUnclamped, 0.0f, cache.GridSize.Z - 1.0f);
// Culling
if ((view.Position - bounds.Center).LengthSquared() >= (options.Distance + bounds.Radius) * (options.Distance + bounds.Radius) || volumeZBoundsMin >= volumeZBoundsMax)
continue;
// Setup material shader data
customData.ParticleIndex = drawCall.Particle.VolumetricFog.ParticleIndex;
bindParams.FirstDrawCall = &drawCall;
drawCall.Material->Bind(bindParams);
// Setup volumetric shader data
PerLight perLight;
auto cb1 = _shader->GetShader()->GetCB(1);
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
perLight.MinZ = volumeZBoundsMin;
perLight.ViewSpaceBoundingSphere = Vector4(viewSpaceLightBoundsOrigin, bounds.Radius);
Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip);
// Upload data
context->UpdateCB(cb1, &perLight);
context->BindCB(1, cb1);
// Call rendering to the volume
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
context->BindIB(_ibCircleRasterize);
context->DrawIndexedInstanced(indexCount, instanceCount, 0);
}
context->ResetRenderTarget();
}
// Render Lights
GPUTextureView* localShadowedLightScattering = nullptr;
@@ -542,13 +601,15 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
// Skip if no lights to render
if (pointLights.Count() + spotLights.Count())
{
PROFILE_GPU("Render Lights");
PROFILE_GPU_CPU("Lights Injection");
// Allocate temporary buffer for light scattering injection
localShadowedLightScattering = GetLocalShadowedLightScattering(renderContext, context, options);
// Prepare
PerLight perLight;
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
auto cb1 = _shader->GetShader()->GetCB(1);
// Bind the output

View File

@@ -11,6 +11,16 @@
/// </summary>
class VolumetricFogPass : public RendererPass<VolumetricFogPass>
{
public:
struct CustomData
{
GPUShader* Shader;
Vector3 GridSize;
float VolumetricFogMaxDistance;
int32 ParticleIndex;
};
private:
PACK_STRUCT(struct SkyLightData {
@@ -53,7 +63,7 @@ private:
});
PACK_STRUCT(struct PerLight {
Vector2 Dummy1;
Vector2 SliceToDepth;
int32 MinZ;
float LocalLightScatteringIntensity;