Fix Volumetric Fog sampling to use the same code path for depth slices conversion
This commit is contained in:
@@ -164,7 +164,7 @@ void PS_Forward(
|
||||
{
|
||||
// Sample volumetric fog and mix it in
|
||||
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, materialInput.WorldPosition - ViewPos, ExponentialHeightFog.VolumetricFogMaxDistance, screenUV);
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, ExponentialHeightFog.VolumetricFogGrid, materialInput.WorldPosition - ViewPos, screenUV);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ float Dummy0;
|
||||
float VolumetricFogMaxDistance;
|
||||
int ParticleStride;
|
||||
int ParticleIndex;
|
||||
float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
float4 GridSliceParameters;
|
||||
@1META_CB_END
|
||||
|
||||
// Particles attributes buffer
|
||||
|
||||
@@ -29,26 +29,8 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
const bool canUseShadow = view.Pass != DrawPass::Depth;
|
||||
|
||||
// Set fog input
|
||||
GPUTextureView* volumetricFogTexture = nullptr;
|
||||
if (cache->Fog)
|
||||
{
|
||||
cache->Fog->GetExponentialHeightFogData(view, data.ExponentialHeightFog);
|
||||
VolumetricFogOptions volumetricFog;
|
||||
cache->Fog->GetVolumetricFogOptions(volumetricFog);
|
||||
if (volumetricFog.UseVolumetricFog() && params.RenderContext.Buffers->VolumetricFog)
|
||||
volumetricFogTexture = params.RenderContext.Buffers->VolumetricFog->ViewVolume();
|
||||
else
|
||||
data.ExponentialHeightFog.VolumetricFogMaxDistance = -1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ExponentialHeightFog.FogMinOpacity = 1.0f;
|
||||
data.ExponentialHeightFog.FogDensity = 0.0f;
|
||||
data.ExponentialHeightFog.FogCutoffDistance = 0.1f;
|
||||
data.ExponentialHeightFog.StartDistance = 0.0f;
|
||||
data.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f;
|
||||
}
|
||||
params.GPUContext->BindSR(volumetricFogTextureRegisterIndex, volumetricFogTexture);
|
||||
data.ExponentialHeightFog = cache->Fog.ExponentialHeightFog;
|
||||
params.GPUContext->BindSR(volumetricFogTextureRegisterIndex, cache->Fog.VolumetricFogTexture);
|
||||
|
||||
// Set directional light input
|
||||
if (cache->DirectionalLights.HasItems())
|
||||
|
||||
@@ -25,8 +25,7 @@ PACK_STRUCT(struct VolumeParticleMaterialShaderData {
|
||||
float VolumetricFogMaxDistance;
|
||||
int32 ParticleStride;
|
||||
int32 ParticleIndex;
|
||||
Float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
Float4 GridSliceParameters;
|
||||
});
|
||||
|
||||
DrawPass VolumeParticleMaterialShader::GetDrawModes() const
|
||||
|
||||
@@ -63,7 +63,7 @@ void ExponentialHeightFog::Draw(RenderContext& renderContext)
|
||||
}
|
||||
|
||||
// Register for Fog Pass
|
||||
renderContext.List->Fog = this;
|
||||
renderContext.List->Fog.Init(renderContext.View, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,19 +187,19 @@ GPU_CB_STRUCT(Data {
|
||||
void ExponentialHeightFog::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output)
|
||||
{
|
||||
PROFILE_GPU_CPU("Exponential Height Fog");
|
||||
auto volumetricFogTexture = renderContext.Buffers->VolumetricFog;
|
||||
auto volumetricFogTexture = renderContext.List->Fog.VolumetricFogTexture;
|
||||
bool useVolumetricFog = volumetricFogTexture != nullptr;
|
||||
|
||||
// Setup shader inputs
|
||||
Data data;
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
GetExponentialHeightFogData(renderContext.View, data.ExponentialHeightFog);
|
||||
data.ExponentialHeightFog = renderContext.List->Fog.ExponentialHeightFog;
|
||||
auto cb = _shader->GetShader()->GetCB(0);
|
||||
ASSERT(cb->GetSize() == sizeof(Data));
|
||||
ASSERT_LOW_LAYER(cb->GetSize() == sizeof(Data));
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
context->BindSR(0, renderContext.Buffers->DepthBuffer);
|
||||
context->BindSR(1, volumetricFogTexture ? volumetricFogTexture->ViewVolume() : nullptr);
|
||||
context->BindSR(1, volumetricFogTexture);
|
||||
|
||||
// TODO: instead of rendering fullscreen triangle, draw quad transformed at the fog start distance (also it could use early depth discard)
|
||||
// TODO: or use DepthBounds to limit the fog rendering to the distance range
|
||||
|
||||
@@ -42,6 +42,8 @@ GPU_CB_STRUCT(ShaderExponentialHeightFogData {
|
||||
float VolumetricFogMaxDistance;
|
||||
float DirectionalInscatteringStartDistance;
|
||||
float StartDistance;
|
||||
|
||||
Float4 VolumetricFogGrid;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -179,6 +179,26 @@ void RenderEnvironmentProbeData::SetShaderData(ShaderEnvProbeData& data) const
|
||||
}
|
||||
}
|
||||
|
||||
RenderFogData::RenderFogData()
|
||||
{
|
||||
Renderer = nullptr;
|
||||
VolumetricFogTexture = nullptr;
|
||||
ExponentialHeightFog.FogMinOpacity = 1.0f;
|
||||
ExponentialHeightFog.FogDensity = 0.0f;
|
||||
ExponentialHeightFog.FogCutoffDistance = 0.1f;
|
||||
ExponentialHeightFog.StartDistance = 0.0f;
|
||||
ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f;
|
||||
ExponentialHeightFog.VolumetricFogMaxDistance = -1.0f;
|
||||
ExponentialHeightFog.VolumetricFogGrid = Float4::One;
|
||||
}
|
||||
|
||||
void RenderFogData::Init(const RenderView& view, IFogRenderer* renderer)
|
||||
{
|
||||
Renderer = renderer;
|
||||
renderer->GetExponentialHeightFogData(view, ExponentialHeightFog);
|
||||
renderer->GetVolumetricFogOptions(VolumetricFog);
|
||||
}
|
||||
|
||||
void* RendererAllocation::Allocate(uintptr size)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -501,7 +521,6 @@ RenderList::RenderList(const SpawnParams& params)
|
||||
, Decals(64)
|
||||
, Sky(nullptr)
|
||||
, AtmosphericFog(nullptr)
|
||||
, Fog(nullptr)
|
||||
, Blendable(32)
|
||||
, ObjectBuffer(0, PixelFormat::R32G32B32A32_Float, false, TEXT("Object Buffer"))
|
||||
, TempObjectBuffer(0, PixelFormat::R32G32B32A32_Float, false, TEXT("Object Buffer"))
|
||||
@@ -534,7 +553,7 @@ void RenderList::Clear()
|
||||
VolumetricFogParticles.Clear();
|
||||
Sky = nullptr;
|
||||
AtmosphericFog = nullptr;
|
||||
Fog = nullptr;
|
||||
Fog = RenderFogData();
|
||||
PostFx.Clear();
|
||||
Settings = PostProcessSettings();
|
||||
Blendable.Clear();
|
||||
|
||||
@@ -175,6 +175,17 @@ struct RenderDecalData
|
||||
uint32 RenderLayersMask;
|
||||
};
|
||||
|
||||
struct RenderFogData
|
||||
{
|
||||
IFogRenderer* Renderer;
|
||||
GPUTextureView* VolumetricFogTexture;
|
||||
ShaderExponentialHeightFogData ExponentialHeightFog;
|
||||
VolumetricFogOptions VolumetricFog;
|
||||
|
||||
RenderFogData();
|
||||
void Init(const RenderView& view, IFogRenderer* renderer);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The draw calls list types.
|
||||
/// </summary>
|
||||
@@ -409,9 +420,9 @@ public:
|
||||
IAtmosphericFogRenderer* AtmosphericFog;
|
||||
|
||||
/// <summary>
|
||||
/// Fog renderer proxy to use (only one per frame)
|
||||
/// Fog rendering data.
|
||||
/// </summary>
|
||||
IFogRenderer* Fog;
|
||||
RenderFogData Fog;
|
||||
|
||||
/// <summary>
|
||||
/// Post effects to render.
|
||||
|
||||
@@ -670,12 +670,12 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
renderContext.List->AtmosphericFog->DrawFog(context, renderContext, *lightBuffer);
|
||||
context->ResetSR();
|
||||
}
|
||||
if (renderContext.List->Fog)
|
||||
if (renderContext.List->Fog.Renderer)
|
||||
{
|
||||
VolumetricFogPass::Instance()->Render(renderContext);
|
||||
|
||||
PROFILE_GPU_CPU("Fog");
|
||||
renderContext.List->Fog->DrawFog(context, renderContext, *lightBuffer);
|
||||
renderContext.List->Fog.Renderer->DrawFog(context, renderContext, *lightBuffer);
|
||||
context->ResetSR();
|
||||
}
|
||||
|
||||
|
||||
@@ -91,22 +91,23 @@ void VolumetricFogPass::Dispose()
|
||||
_shader = nullptr;
|
||||
}
|
||||
|
||||
Float3 GetGridSliceParameters(float fogStart, float fogEnd, int32 gridSizeZ)
|
||||
Float4 GetGridSliceParameters(float fogStart, float fogEnd, int32 gridSizeZ)
|
||||
{
|
||||
float sliceToUV = 1.0f / (float)gridSizeZ;
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
float sliceToDepth = fogEnd / (float)gridSizeZ;
|
||||
return Float3(sliceToDepth, 1.0f / sliceToDepth, 0.0f);
|
||||
return Float4(sliceToDepth, 1.0f / sliceToDepth, 0.0f, sliceToUV);
|
||||
#else
|
||||
// Use logarithmic distribution for Z slices to have more resolution for close distances and less for far ones (less aliasing near camera)
|
||||
const float distribution = 220.0f; // Manually adjusted to give a good distribution across the range
|
||||
fogStart += UNITS_TO_METERS(10); // Bias start a bit for some more quality
|
||||
float y = (fogEnd - fogStart * Math::Exp2((float)(gridSizeZ - 1) / distribution)) / (fogEnd - fogStart);
|
||||
float x = (1.0f - y) / fogStart;
|
||||
return Float3(x, y, distribution);
|
||||
return Float4(x, y, distribution, sliceToUV);
|
||||
#endif
|
||||
}
|
||||
|
||||
float GetDepthFromSlice(float slice, const Float3& gridSliceParameters)
|
||||
float GetDepthFromSlice(float slice, const Float4& gridSliceParameters)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return slice * gridSliceParameters.X;
|
||||
@@ -115,7 +116,7 @@ float GetDepthFromSlice(float slice, const Float3& gridSliceParameters)
|
||||
#endif
|
||||
}
|
||||
|
||||
float GetSliceFromDepth(float sceneDepth, const Float3& gridSliceParameters)
|
||||
float GetSliceFromDepth(float sceneDepth, const Float4& gridSliceParameters)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return sceneDepth * gridSliceParameters.Y;
|
||||
@@ -135,33 +136,21 @@ struct alignas(Float4) RasterizeSphere
|
||||
|
||||
bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context)
|
||||
{
|
||||
const auto fog = renderContext.List->Fog;
|
||||
auto& options = _cache.Options;
|
||||
|
||||
// Check if already prepared for this frame
|
||||
const auto& fog = renderContext.List->Fog;
|
||||
if (renderContext.Buffers->LastFrameVolumetricFog == Engine::FrameCount)
|
||||
{
|
||||
if (fog)
|
||||
fog->GetVolumetricFogOptions(options);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if skip rendering
|
||||
if (fog == nullptr || !renderContext.List->Setup.UseVolumetricFog || !_isSupported || checkIfSkipPass())
|
||||
{
|
||||
RenderTargetPool::Release(renderContext.Buffers->VolumetricFog);
|
||||
renderContext.Buffers->VolumetricFog = nullptr;
|
||||
renderContext.Buffers->LastFrameVolumetricFog = 0;
|
||||
return true;
|
||||
}
|
||||
fog->GetVolumetricFogOptions(options);
|
||||
if (!options.UseVolumetricFog())
|
||||
if (fog.Renderer == nullptr ||
|
||||
!renderContext.List->Setup.UseVolumetricFog ||
|
||||
!_isSupported ||
|
||||
!fog.VolumetricFog.UseVolumetricFog() ||
|
||||
checkIfSkipPass())
|
||||
{
|
||||
RenderTargetPool::Release(renderContext.Buffers->VolumetricFog);
|
||||
renderContext.Buffers->VolumetricFog = nullptr;
|
||||
renderContext.Buffers->LastFrameVolumetricFog = 0;
|
||||
return true;
|
||||
}
|
||||
auto& options = fog.VolumetricFog;
|
||||
|
||||
// Setup configuration
|
||||
_cache.FogJitter = true;
|
||||
@@ -220,6 +209,13 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context)
|
||||
_cache.Data.HistoryWeight = _cache.HistoryWeight;
|
||||
_cache.Data.FogParameters = options.FogParameters;
|
||||
_cache.Data.GridSliceParameters = GetGridSliceParameters(renderContext.View.Near, options.Distance, _cache.GridSizeZ);
|
||||
/*static bool log = true;
|
||||
if (log)
|
||||
{
|
||||
log = false;
|
||||
for (int slice = 0; slice < _cache.GridSizeZ; slice++)
|
||||
LOG(Error, "Slice {} -> {}", slice, GetDepthFromSlice((float)slice, _cache.Data.GridSliceParameters));
|
||||
}*/
|
||||
_cache.Data.InverseSquaredLightDistanceBiasScale = _cache.InverseSquaredLightDistanceBiasScale;
|
||||
_cache.Data.PhaseG = options.ScatteringDistribution;
|
||||
_cache.Data.VolumetricFogMaxDistance = options.Distance;
|
||||
@@ -287,7 +283,7 @@ bool VolumetricFogPass::InitSphereRasterize(RasterizeSphere& sphere, RenderView&
|
||||
sphere.VolumeZBoundsMax = (uint16)Math::Clamp(furthestSliceIndex, 0.0f, _cache.GridSize.Z - 1.0f);
|
||||
|
||||
// Cull
|
||||
if ((view.Position - sphere.Center).LengthSquared() >= Math::Square(_cache.Options.Distance + sphere.Radius) ||
|
||||
if ((view.Position - sphere.Center).LengthSquared() >= Math::Square(_cache.Data.VolumetricFogMaxDistance + sphere.Radius) ||
|
||||
sphere.VolumeZBoundsMin > sphere.VolumeZBoundsMax)
|
||||
{
|
||||
return true;
|
||||
@@ -508,7 +504,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
// Get lights to render
|
||||
Array<uint16, InlinedAllocation<64, RendererAllocation>> pointLights;
|
||||
Array<uint16, InlinedAllocation<64, RendererAllocation>> spotLights;
|
||||
float distance = _cache.Options.Distance;
|
||||
float distance = cache.Data.VolumetricFogMaxDistance;
|
||||
for (int32 i = 0; i < renderContext.List->PointLights.Count(); i++)
|
||||
{
|
||||
const auto& light = renderContext.List->PointLights.Get()[i];
|
||||
@@ -617,6 +613,10 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
}
|
||||
renderContext.Buffers->LastFrameVolumetricFog = Engine::FrameCount;
|
||||
|
||||
// Update fog to be used by other passes
|
||||
renderContext.List->Fog.VolumetricFogTexture = integratedLightScattering->ViewVolume();
|
||||
renderContext.List->Fog.ExponentialHeightFog.VolumetricFogGrid = cache.Data.GridSliceParameters;
|
||||
|
||||
groupCountX = Math::DivideAndRoundUp((int32)cache.GridSize.X, VolumetricFogIntegrationGroupSize);
|
||||
groupCountY = Math::DivideAndRoundUp((int32)cache.GridSize.Y, VolumetricFogIntegrationGroupSize);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
GPUShader* Shader;
|
||||
Float3 GridSize;
|
||||
float VolumetricFogMaxDistance;
|
||||
Float3 GridSliceParameters;
|
||||
Float4 GridSliceParameters;
|
||||
int32 ParticleIndex;
|
||||
};
|
||||
|
||||
@@ -58,8 +58,7 @@ private:
|
||||
float InverseSquaredLightDistanceBiasScale;
|
||||
|
||||
Float4 FogParameters;
|
||||
Float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
Float4 GridSliceParameters;
|
||||
|
||||
Matrix PrevWorldToClip;
|
||||
|
||||
@@ -133,11 +132,6 @@ private:
|
||||
|
||||
float SphereRasterizeRadiusBias;
|
||||
|
||||
/// <summary>
|
||||
/// Fog options(from renderer).
|
||||
/// </summary>
|
||||
VolumetricFogOptions Options;
|
||||
|
||||
/// <summary>
|
||||
/// The cached per-frame data for the constant buffer.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,6 +27,8 @@ struct ExponentialHeightFogData
|
||||
float VolumetricFogMaxDistance;
|
||||
float DirectionalInscatteringStartDistance;
|
||||
float StartDistance;
|
||||
|
||||
float4 VolumetricFogGrid;
|
||||
};
|
||||
|
||||
float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance, float sceneDistance)
|
||||
|
||||
@@ -46,7 +46,7 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0
|
||||
|
||||
#if VOLUMETRIC_FOG
|
||||
// Sample volumetric fog and mix it in
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, worldPos - GBuffer.ViewPos, ExponentialHeightFog.VolumetricFogMaxDistance, input.TexCoord);
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, ExponentialHeightFog.VolumetricFogGrid, worldPos - GBuffer.ViewPos, input.TexCoord);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#define VOLUMETRIC_FOG_GRID_Z_LINEAR 1
|
||||
|
||||
float GetDepthFromSlice(float3 gridSliceParameters, float zSlice)
|
||||
float GetDepthFromSlice(float4 gridSliceParameters, float zSlice)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return zSlice * gridSliceParameters.x;
|
||||
@@ -14,7 +14,7 @@ float GetDepthFromSlice(float3 gridSliceParameters, float zSlice)
|
||||
#endif
|
||||
}
|
||||
|
||||
float GetSliceFromDepth(float3 gridSliceParameters, float sceneDepth)
|
||||
float GetSliceFromDepth(float4 gridSliceParameters, float sceneDepth)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return sceneDepth * gridSliceParameters.y;
|
||||
@@ -23,11 +23,10 @@ float GetSliceFromDepth(float3 gridSliceParameters, float sceneDepth)
|
||||
#endif
|
||||
}
|
||||
|
||||
float4 SampleVolumetricFog(Texture3D volumetricFogTexture, float3 viewVector, float maxDistance, float2 uv)
|
||||
float4 SampleVolumetricFog(Texture3D volumetricFogTexture, float4 gridSliceParameters, float3 viewVector, float2 uv)
|
||||
{
|
||||
float sceneDepth = length(viewVector);
|
||||
float zSlice = sceneDepth / maxDistance;
|
||||
// TODO: use GetSliceFromDepth instead to handle non-linear depth distributions
|
||||
float zSlice = GetSliceFromDepth(gridSliceParameters, sceneDepth) * gridSliceParameters.w;
|
||||
float3 volumeUV = float3(uv, zSlice);
|
||||
return volumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ float VolumetricFogMaxDistance;
|
||||
float InverseSquaredLightDistanceBiasScale;
|
||||
|
||||
float4 FogParameters;
|
||||
float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
float4 GridSliceParameters;
|
||||
|
||||
float4x4 PrevWorldToClip;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user