diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 638133818..2a2bc2a17 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:706adde844360c2b0e65ecabaf1d2e2cc4eb4ac7ca9d483d0dd04ec8163b3d97 -size 13081 +oid sha256:a8ed390d9a555003ff888c3fce208c6a3d413aa55f230566eb3f6cfe6d5f90df +size 13195 diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 4b2058047..1fc114192 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -47,8 +47,8 @@ GPU_CB_STRUCT(Data { uint32 GridSizeIntZ; float PhaseG; - Float2 Dummy0; - float VolumetricFogMaxDistance; + Float2 VolumetricFogRange; + float Dummy0; float InverseSquaredLightDistanceBiasScale; Float4 FogParameters; @@ -161,8 +161,9 @@ 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 Float4(sliceToDepth, 1.0f / sliceToDepth, 0.0f, sliceToUV); + float sliceToDepth = (fogEnd - fogStart) / (float)(gridSizeZ - 1); + float sliceToDepthInv = 1.0f / sliceToDepth; + return Float4(sliceToDepth, 1.0f / sliceToDepth, -fogStart * sliceToDepthInv, 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 @@ -176,7 +177,7 @@ Float4 GetGridSliceParameters(float fogStart, float fogEnd, int32 gridSizeZ) float GetDepthFromSlice(float slice, const Float4& gridSliceParameters) { #if VOLUMETRIC_FOG_GRID_Z_LINEAR - return slice * gridSliceParameters.X; + return (slice - gridSliceParameters.Z) * gridSliceParameters.X; #else return (Math::Exp2(slice / gridSliceParameters.Z) - gridSliceParameters.Y) / gridSliceParameters.X; #endif @@ -185,7 +186,7 @@ float GetDepthFromSlice(float slice, const Float4& gridSliceParameters) float GetSliceFromDepth(float sceneDepth, const Float4& gridSliceParameters) { #if VOLUMETRIC_FOG_GRID_Z_LINEAR - return sceneDepth * gridSliceParameters.Y; + return sceneDepth * gridSliceParameters.Y + gridSliceParameters.Z; #else return Math::Log2(sceneDepth * gridSliceParameters.X + gridSliceParameters.Y) * gridSliceParameters.Z; #endif @@ -205,10 +206,12 @@ bool VolumetricFogPass::Init(FrameCache& cache, RenderContext& renderContext, GP const auto& fog = renderContext.List->Fog; if (renderContext.Buffers->LastFrameVolumetricFog == Engine::FrameCount) return false; + float fogStart = Math::Max(renderContext.View.Near, fog.ExponentialHeightFogData.StartDistance); if (fog.Renderer == nullptr || !renderContext.List->Setup.UseVolumetricFog || !_isSupported || !fog.VolumetricFog.UseVolumetricFog() || + fogStart + METERS_TO_UNITS(1) >= fog.VolumetricFog.Distance || checkIfSkipPass()) { RenderTargetPool::Release(renderContext.Buffers->VolumetricFog); @@ -245,6 +248,13 @@ bool VolumetricFogPass::Init(FrameCache& cache, RenderContext& renderContext, GP cache.MissedHistorySamplesCount = 8; break; } + auto& fogData = renderContext.Buffers->VolumetricFogData; + fogData.MaxDistance = options.Distance; + + // Reduce Z resolution when fog range is smaller than reference + float referenceRangeScaleZ = METERS_TO_UNITS(60); + float fogRange = fogData.MaxDistance - fogStart; + cache.GridSizeZ = Math::CeilToInt(Math::Clamp(fogRange / referenceRangeScaleZ, 0.3f, 1.5f) * cache.GridSizeZ); // Calculate volumetric fog size const int32 width = renderContext.Buffers->GetWidth(); @@ -265,8 +275,6 @@ bool VolumetricFogPass::Init(FrameCache& cache, RenderContext& renderContext, GP (float)Math::DivideAndRoundUp(width, cache.GridPixelSize), (float)Math::DivideAndRoundUp(height, cache.GridPixelSize), (float)cache.GridSizeZ); - auto& fogData = renderContext.Buffers->VolumetricFogData; - fogData.MaxDistance = options.Distance; if (renderContext.Task->IsCameraCut || renderContext.View.IsOriginTeleport() || (renderContext.Buffers->VolumetricFog && renderContext.Buffers->VolumetricFog->Size3() != cache.GridSize) || @@ -287,17 +295,22 @@ bool VolumetricFogPass::Init(FrameCache& cache, RenderContext& renderContext, GP cache.Data.GridSizeIntZ = (uint32)cache.GridSize.Z; cache.Data.HistoryWeight = cache.HistoryWeight; cache.Data.FogParameters = options.FogParameters; - cache.Data.GridSliceParameters = GetGridSliceParameters(renderContext.View.Near, options.Distance, cache.GridSizeZ); + cache.Data.GridSliceParameters = GetGridSliceParameters(fogStart, fogData.MaxDistance, cache.GridSizeZ); /*static bool log = true; if (log) { log = false; + LOG(Info, "Fog range: {} - {}", fogStart, fogData.MaxDistance); for (int slice = 0; slice < cache.GridSizeZ; slice++) - LOG(Error, "Slice {} -> {}", slice, GetDepthFromSlice((float)slice, cache.Data.GridSliceParameters)); + { + float depth = GetDepthFromSlice((float)slice, cache.Data.GridSliceParameters); + LOG(Info, "Slice {} -> {}", slice, depth); + ASSERT((int32)GetSliceFromDepth(depth, cache.Data.GridSliceParameters) == slice); + } }*/ cache.Data.InverseSquaredLightDistanceBiasScale = cache.InverseSquaredLightDistanceBiasScale; cache.Data.PhaseG = options.ScatteringDistribution; - cache.Data.VolumetricFogMaxDistance = options.Distance; + cache.Data.VolumetricFogRange = Float2(fogStart, options.Distance); cache.Data.MissedHistorySamplesCount = Math::Clamp(cache.MissedHistorySamplesCount, 1, (int32)ARRAY_COUNT(cache.Data.FrameJitterOffsets)); Matrix::Transpose(renderContext.View.PrevViewProjection, cache.Data.PrevWorldToClip); cache.Data.SkyLight.VolumetricScatteringIntensity = 0; @@ -362,7 +375,7 @@ bool VolumetricFogPass::InitSphereRasterize(FrameCache& cache, RasterizeSphere& sphere.VolumeZBoundsMax = (uint16)Math::Clamp(furthestSliceIndex, 0.0f, cache.GridSize.Z - 1.0f); // Cull - if ((view.Position - sphere.Center).LengthSquared() >= Math::Square(cache.Data.VolumetricFogMaxDistance + sphere.Radius) || + if ((view.Position - sphere.Center).LengthSquared() >= Math::Square(cache.Data.VolumetricFogRange.Y + sphere.Radius) || sphere.VolumeZBoundsMin > sphere.VolumeZBoundsMax) { return true; @@ -395,7 +408,7 @@ void VolumetricFogPass::RenderRadialLight(FrameCache& cache, RenderContext& rend // Setup data perLight.SliceToDepth.X = cache.Data.GridSize.Z; - perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance; + perLight.SliceToDepth.Y = cache.Data.VolumetricFogRange.Y; perLight.MinZ = sphere.VolumeZBoundsMin; perLight.LocalLightScatteringIntensity = light.VolumetricScatteringIntensity; perLight.ViewSpaceBoundingSphere = Float4(sphere.ViewSpaceCenter, sphere.Radius); @@ -532,7 +545,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) CustomData customData; customData.Shader = _shader->GetShader(); customData.GridSize = cache.GridSize; - customData.VolumetricFogMaxDistance = cache.Data.VolumetricFogMaxDistance; + customData.VolumetricFogMaxDistance = cache.Data.VolumetricFogRange.Y; customData.GridSliceParameters = cache.Data.GridSliceParameters; bindParams.CustomData = &customData; bindParams.BindViewData(); @@ -554,7 +567,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) PerLight perLight; auto cb2 = _shader->GetShader()->GetCB(2); perLight.SliceToDepth.X = cache.Data.GridSize.Z; - perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance; + perLight.SliceToDepth.Y = cache.Data.VolumetricFogRange.Y; perLight.MinZ = sphere.VolumeZBoundsMin; perLight.ViewSpaceBoundingSphere = Float4(sphere.ViewSpaceCenter, sphere.Radius); Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip); @@ -582,7 +595,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) Array> pointLights; Array> spotLights; Float3 viewPosition = renderContext.View.Position; - float distance = cache.Data.VolumetricFogMaxDistance; + float distance = cache.Data.VolumetricFogRange.Y; for (int32 i = 0; i < renderContext.List->PointLights.Count(); i++) { const auto& light = renderContext.List->PointLights.Get()[i]; @@ -609,7 +622,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) // Prepare PerLight perLight; perLight.SliceToDepth.X = cache.Data.GridSize.Z; - perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance; + perLight.SliceToDepth.Y = cache.Data.VolumetricFogRange.Y; auto cb2 = _shader->GetShader()->GetCB(2); // Bind the output diff --git a/Source/Shaders/VolumetricFog.hlsl b/Source/Shaders/VolumetricFog.hlsl index 22bb71f3c..ad74a10c5 100644 --- a/Source/Shaders/VolumetricFog.hlsl +++ b/Source/Shaders/VolumetricFog.hlsl @@ -18,7 +18,7 @@ struct VolumetricFogData float GetDepthFromSlice(float4 gridSliceParameters, float zSlice) { #if VOLUMETRIC_FOG_GRID_Z_LINEAR - return zSlice * gridSliceParameters.x; + return (zSlice - gridSliceParameters.z) * gridSliceParameters.x; #else return (exp2(zSlice / gridSliceParameters.z) - gridSliceParameters.y) / gridSliceParameters.x; #endif @@ -27,7 +27,7 @@ float GetDepthFromSlice(float4 gridSliceParameters, float zSlice) float GetSliceFromDepth(float4 gridSliceParameters, float sceneDepth) { #if VOLUMETRIC_FOG_GRID_Z_LINEAR - return sceneDepth * gridSliceParameters.y; + return sceneDepth * gridSliceParameters.y + gridSliceParameters.z; #else return (log2(sceneDepth * gridSliceParameters.x + gridSliceParameters.y) * gridSliceParameters.z); #endif diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index 49c198c64..c9facbdd7 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -47,8 +47,8 @@ uint MissedHistorySamplesCount; uint3 GridSizeInt; float PhaseG; -float2 Dummy0; -float VolumetricFogMaxDistance; +float2 VolumetricFogRange; +float Dummy0; float InverseSquaredLightDistanceBiasScale; float4 FogParameters; @@ -104,7 +104,8 @@ float3 GetVolumeUV(float3 worldPosition, float4x4 worldToClip) { float4 ndcPosition = mul(float4(worldPosition, 1), worldToClip); ndcPosition.xy /= ndcPosition.w; - return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ndcPosition.w / VolumetricFogMaxDistance); + ndcPosition.w = (ndcPosition.w - VolumetricFogRange.x) / VolumetricFogRange.y; // TODO: convert into MAD + return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ndcPosition.w); } // Vertex shader that writes to a range of slices of a volume texture @@ -340,7 +341,7 @@ void CS_FinalIntegration(uint3 DispatchThreadId : SV_DispatchThreadID) if (any(gridCoordinate.xy >= GridSizeInt.xy)) return; float4 acc = float4(0, 0, 0, 1); - float3 prevPositionWS = GBuffer.ViewPos; + float3 prevPositionWS = GetCellPositionWS(uint3(gridCoordinate.xy, 0), 0.5f); for (uint layerIndex = 0; layerIndex < GridSizeInt.z; layerIndex++) {