Add improvements to Volumetric Fog quality and performance
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "./Flax/Lighting.hlsl"
|
||||
#include "./Flax/ShadowsSampling.hlsl"
|
||||
#include "./Flax/ExponentialHeightFog.hlsl"
|
||||
#include "./Flax/VolumetricFog.hlsl"
|
||||
@2// Forward Shading: Constants
|
||||
LightData DirectionalLight;
|
||||
LightData SkyLight;
|
||||
@@ -159,16 +160,11 @@ void PS_Forward(
|
||||
float fogSceneDistance = gBuffer.ViewPos.z;
|
||||
#endif
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, fogSceneDistance);
|
||||
|
||||
if (ExponentialHeightFog.VolumetricFogMaxDistance > 0)
|
||||
{
|
||||
// Sample volumetric fog and mix it in
|
||||
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||
float3 viewVector = materialInput.WorldPosition - ViewPos;
|
||||
float sceneDepth = length(viewVector);
|
||||
float depthSlice = sceneDepth / ExponentialHeightFog.VolumetricFogMaxDistance;
|
||||
float3 volumeUV = float3(screenUV, depthSlice);
|
||||
float4 volumetricFog = VolumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, materialInput.WorldPosition - ViewPos, ExponentialHeightFog.VolumetricFogMaxDistance, screenUV);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/MaterialCommon.hlsl"
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
#include "./Flax/VolumetricFog.hlsl"
|
||||
@7
|
||||
|
||||
// Primary constant buffer (with additional material parameters)
|
||||
@@ -21,6 +22,8 @@ float Dummy0;
|
||||
float VolumetricFogMaxDistance;
|
||||
int ParticleStride;
|
||||
int ParticleIndex;
|
||||
float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
@1META_CB_END
|
||||
|
||||
// Particles attributes buffer
|
||||
@@ -202,19 +205,19 @@ Material GetMaterialPS(MaterialInput input)
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
void PS_VolumetricFog(Quad_GS2PS input, out float4 VBufferA : SV_Target0, out float4 VBufferB : SV_Target1)
|
||||
{
|
||||
// Reproject grid position back to the screen and world space
|
||||
uint3 gridCoordinate = uint3(input.Vertex.Position.xy, input.LayerIndex);
|
||||
float3 cellOffset = 0.5f;
|
||||
float2 volumeUV = (gridCoordinate.xy + cellOffset.xy) / GridSize.xy;
|
||||
float zSlice = gridCoordinate.z + cellOffset.z;
|
||||
float sceneDepth = (zSlice / GridSize.z) * VolumetricFogMaxDistance / ViewFar;
|
||||
float sceneDepth = GetDepthFromSlice(GridSliceParameters, gridCoordinate.z + cellOffset.z) / ViewFar;
|
||||
float deviceDepth = (ViewInfo.w / sceneDepth) + ViewInfo.z;
|
||||
float4 clipPos = float4(volumeUV * float2(2.0, -2.0) + float2(-1.0, 1.0), deviceDepth, 1.0);
|
||||
float4 wsPos = mul(clipPos, InverseViewProjectionMatrix);
|
||||
float3 positionWS = wsPos.xyz / wsPos.w;
|
||||
wsPos.xyz /= wsPos.w;
|
||||
|
||||
// Get material parameters
|
||||
MaterialInput materialInput = (MaterialInput)0;
|
||||
materialInput.WorldPosition = positionWS;
|
||||
materialInput.WorldPosition = wsPos.xyz;
|
||||
materialInput.TexCoord = input.Vertex.TexCoord;
|
||||
materialInput.ParticleIndex = ParticleIndex;
|
||||
materialInput.TBN = float3x3(float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1));
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 180
|
||||
#define MATERIAL_GRAPH_VERSION 181
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -25,6 +25,8 @@ PACK_STRUCT(struct VolumeParticleMaterialShaderData {
|
||||
float VolumetricFogMaxDistance;
|
||||
int32 ParticleStride;
|
||||
int32 ParticleIndex;
|
||||
Float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
});
|
||||
|
||||
DrawPass VolumeParticleMaterialShader::GetDrawModes() const
|
||||
@@ -86,6 +88,7 @@ void VolumeParticleMaterialShader::Bind(BindParameters& params)
|
||||
materialData->VolumetricFogMaxDistance = customData->VolumetricFogMaxDistance;
|
||||
materialData->ParticleStride = drawCall.Particle.Particles->Stride;
|
||||
materialData->ParticleIndex = customData->ParticleIndex;
|
||||
materialData->GridSliceParameters = customData->GridSliceParameters;
|
||||
}
|
||||
|
||||
// Bind constants
|
||||
|
||||
@@ -630,6 +630,20 @@ void RenderTools::ComputeBoxModelDrawMatrix(const RenderView& view, const Orient
|
||||
resultIsViewInside = box.Contains(view.Position) == ContainmentType::Contains;
|
||||
}
|
||||
|
||||
float RenderTools::TemporalHalton(int32 index, int32 base)
|
||||
{
|
||||
float result = 0.0f;
|
||||
const float invBase = 1.0f / (float)base;
|
||||
float fraction = invBase;
|
||||
while (index > 0)
|
||||
{
|
||||
result += float(index % base) * fraction;
|
||||
index /= base;
|
||||
fraction *= invBase;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Float2 RenderTools::GetDepthBounds(const RenderView& view, const Float3& nearPoint, const Float3& farPoint)
|
||||
{
|
||||
// Point closest the view
|
||||
|
||||
@@ -148,6 +148,8 @@ public:
|
||||
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
static void ComputeBoxModelDrawMatrix(const RenderView& view, const OrientedBoundingBox& box, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
|
||||
static float TemporalHalton(int32 index, int32 base);
|
||||
|
||||
// Calculates depth bounds to optimize drawing with depth buffer to cover only specific range of depth. Returns min and max depth (as Float2) to pass into GPUContext::SetDepthBounds.
|
||||
static Float2 GetDepthBounds(const RenderView& view, const Float3& nearPoint, const Float3& farPoint);
|
||||
static Float2 GetDepthBounds(const RenderView& view, const BoundingSphere& bounds);
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "Engine/Level/Actors/Camera.h"
|
||||
#include "Engine/Core/Math/Double4x4.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/RendererPass.h"
|
||||
#include "RenderBuffers.h"
|
||||
#include "RenderTask.h"
|
||||
#include "RenderTools.h"
|
||||
|
||||
void RenderView::Prepare(RenderContext& renderContext)
|
||||
{
|
||||
@@ -29,8 +29,8 @@ void RenderView::Prepare(RenderContext& renderContext)
|
||||
|
||||
// Calculate jitter
|
||||
const float jitterSpread = renderContext.List->Settings.AntiAliasing.TAA_JitterSpread;
|
||||
const float jitterX = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 2) - 0.5f) * jitterSpread;
|
||||
const float jitterY = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 3) - 0.5f) * jitterSpread;
|
||||
const float jitterX = (RenderTools::TemporalHalton(TaaFrameIndex + 1, 2) - 0.5f) * jitterSpread;
|
||||
const float jitterY = (RenderTools::TemporalHalton(TaaFrameIndex + 1, 3) - 0.5f) * jitterSpread;
|
||||
taaJitter = Float2(jitterX * 2.0f / width, jitterY * 2.0f / height);
|
||||
|
||||
// Modify projection matrix
|
||||
|
||||
@@ -187,8 +187,8 @@ GPU_CB_STRUCT(Data {
|
||||
void ExponentialHeightFog::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output)
|
||||
{
|
||||
PROFILE_GPU_CPU("Exponential Height Fog");
|
||||
auto integratedLightScattering = renderContext.Buffers->VolumetricFog;
|
||||
bool useVolumetricFog = integratedLightScattering != nullptr;
|
||||
auto volumetricFogTexture = renderContext.Buffers->VolumetricFog;
|
||||
bool useVolumetricFog = volumetricFogTexture != nullptr;
|
||||
|
||||
// Setup shader inputs
|
||||
Data data;
|
||||
@@ -199,9 +199,10 @@ void ExponentialHeightFog::DrawFog(GPUContext* context, RenderContext& renderCon
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
context->BindSR(0, renderContext.Buffers->DepthBuffer);
|
||||
context->BindSR(1, integratedLightScattering ? integratedLightScattering->ViewVolume() : nullptr);
|
||||
context->BindSR(1, volumetricFogTexture ? volumetricFogTexture->ViewVolume() : nullptr);
|
||||
|
||||
// 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
|
||||
|
||||
// Draw fog
|
||||
const int32 psIndex = (useVolumetricFog ? 1 : 0);
|
||||
|
||||
@@ -13,25 +13,6 @@
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#include "Config.h"
|
||||
|
||||
class RendererUtils
|
||||
{
|
||||
public:
|
||||
|
||||
static float TemporalHalton(int32 index, int32 base)
|
||||
{
|
||||
float result = 0.0f;
|
||||
const float invBase = 1.0f / base;
|
||||
float fraction = invBase;
|
||||
while (index > 0)
|
||||
{
|
||||
result += (index % base) * fraction;
|
||||
index /= base;
|
||||
fraction *= invBase;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Base class for renderer components called render pass.
|
||||
/// Each render pass supports proper resources initialization and disposing.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "GBufferPass.h"
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
@@ -13,14 +14,12 @@
|
||||
#include "Engine/Content/Assets/CubeTexture.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/Units.h"
|
||||
|
||||
// Must match shader source
|
||||
int32 VolumetricFogGridInjectionGroupSize = 4;
|
||||
int32 VolumetricFogIntegrationGroupSize = 8;
|
||||
|
||||
VolumetricFogPass::VolumetricFogPass()
|
||||
{
|
||||
}
|
||||
#define VOLUMETRIC_FOG_GRID_Z_LINEAR 1
|
||||
|
||||
String VolumetricFogPass::ToString() const
|
||||
{
|
||||
@@ -92,14 +91,52 @@ void VolumetricFogPass::Dispose()
|
||||
_shader = nullptr;
|
||||
}
|
||||
|
||||
float ComputeZSliceFromDepth(float sceneDepth, const VolumetricFogOptions& options, int32 gridSizeZ)
|
||||
Float3 GetGridSliceParameters(float fogStart, float fogEnd, int32 gridSizeZ)
|
||||
{
|
||||
return sceneDepth / options.Distance * (float)gridSizeZ;
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
float sliceToDepth = fogEnd / (float)gridSizeZ;
|
||||
return Float3(sliceToDepth, 1.0f / sliceToDepth, 0.0f);
|
||||
#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);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options)
|
||||
float GetDepthFromSlice(float slice, const Float3& gridSliceParameters)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return slice * gridSliceParameters.X;
|
||||
#else
|
||||
return (Math::Exp2(slice / gridSliceParameters.Z) - gridSliceParameters.Y) / gridSliceParameters.X;
|
||||
#endif
|
||||
}
|
||||
|
||||
float GetSliceFromDepth(float sceneDepth, const Float3& gridSliceParameters)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return sceneDepth * gridSliceParameters.Y;
|
||||
#else
|
||||
return Math::Log2(sceneDepth * gridSliceParameters.X + gridSliceParameters.Y) * gridSliceParameters.Z;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct alignas(Float4) RasterizeSphere
|
||||
{
|
||||
Float3 Center;
|
||||
float Radius;
|
||||
Float3 ViewSpaceCenter;
|
||||
uint16 VolumeZBoundsMin;
|
||||
uint16 VolumeZBoundsMax;
|
||||
};
|
||||
|
||||
bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context)
|
||||
{
|
||||
const auto fog = renderContext.List->Fog;
|
||||
auto& options = _cache.Options;
|
||||
|
||||
// Check if already prepared for this frame
|
||||
if (renderContext.Buffers->LastFrameVolumetricFog == Engine::FrameCount)
|
||||
@@ -127,33 +164,29 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
}
|
||||
|
||||
// Setup configuration
|
||||
_cache.HistoryWeight = 0.9f;
|
||||
_cache.FogJitter = true;
|
||||
_cache.HistoryWeight = 0.92f;
|
||||
_cache.InverseSquaredLightDistanceBiasScale = 1.0f;
|
||||
const auto quality = Graphics::VolumetricFogQuality;
|
||||
switch (quality)
|
||||
switch (Graphics::VolumetricFogQuality)
|
||||
{
|
||||
case Quality::Low:
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 64;
|
||||
_cache.FogJitter = false;
|
||||
_cache.GridPixelSize = 24;
|
||||
_cache.GridSizeZ = 50;
|
||||
_cache.MissedHistorySamplesCount = 1;
|
||||
break;
|
||||
case Quality::Medium:
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 64;
|
||||
_cache.FogJitter = true;
|
||||
_cache.MissedHistorySamplesCount = 4;
|
||||
_cache.GridPixelSize = 20;
|
||||
_cache.GridSizeZ = 54;
|
||||
_cache.MissedHistorySamplesCount = 2;
|
||||
break;
|
||||
case Quality::High:
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 128;
|
||||
_cache.FogJitter = true;
|
||||
_cache.GridSizeZ = 64;
|
||||
_cache.MissedHistorySamplesCount = 4;
|
||||
break;
|
||||
case Quality::Ultra:
|
||||
_cache.GridPixelSize = 8;
|
||||
_cache.GridSizeZ = 128;
|
||||
_cache.FogJitter = true;
|
||||
_cache.MissedHistorySamplesCount = 8;
|
||||
break;
|
||||
}
|
||||
@@ -186,6 +219,7 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
_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.InverseSquaredLightDistanceBiasScale = _cache.InverseSquaredLightDistanceBiasScale;
|
||||
_cache.Data.PhaseG = options.ScatteringDistribution;
|
||||
_cache.Data.VolumetricFogMaxDistance = options.Distance;
|
||||
@@ -196,20 +230,24 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
// Fill frame jitter history
|
||||
const Float4 defaultOffset(0.5f, 0.5f, 0.5f, 0.0f);
|
||||
for (int32 i = 0; i < ARRAY_COUNT(_cache.Data.FrameJitterOffsets); i++)
|
||||
{
|
||||
_cache.Data.FrameJitterOffsets[i] = defaultOffset;
|
||||
}
|
||||
_cache.SphereRasterizeRadiusBias = 0.0f;
|
||||
if (_cache.FogJitter)
|
||||
{
|
||||
for (int32 i = 0; i < _cache.MissedHistorySamplesCount; i++)
|
||||
{
|
||||
const uint64 frameNumber = renderContext.Task->LastUsedFrame - i;
|
||||
_cache.Data.FrameJitterOffsets[i] = Float4(
|
||||
RendererUtils::TemporalHalton(frameNumber & 1023, 2),
|
||||
RendererUtils::TemporalHalton(frameNumber & 1023, 3),
|
||||
RendererUtils::TemporalHalton(frameNumber & 1023, 5),
|
||||
RenderTools::TemporalHalton(frameNumber & 1023, 2),
|
||||
RenderTools::TemporalHalton(frameNumber & 1023, 3),
|
||||
RenderTools::TemporalHalton(frameNumber & 1023, 5),
|
||||
0);
|
||||
}
|
||||
|
||||
// Add bias to radius when using jittering to avoid pixelization on the circle borders (cell offset is randomized)
|
||||
float worldUnitsPerDepthCell = options.Distance / _cache.GridSize.Z;
|
||||
// TODO: include XY size too?
|
||||
_cache.SphereRasterizeRadiusBias = worldUnitsPerDepthCell * 0.25f;
|
||||
}
|
||||
|
||||
// Set constant buffer data
|
||||
@@ -235,7 +273,30 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const
|
||||
bool VolumetricFogPass::InitSphereRasterize(RasterizeSphere& sphere, RenderView& view, const Float3& center, float radius)
|
||||
{
|
||||
ASSERT_LOW_LAYER(!center.IsNanOrInfinity() && !isnan(radius) && !isinf(radius));
|
||||
sphere.Center = center;
|
||||
sphere.Radius = radius + _cache.SphereRasterizeRadiusBias;
|
||||
|
||||
// Calculate sphere volume bounds in camera frustum depth range (min and max)
|
||||
sphere.ViewSpaceCenter = Float3::Transform(center, view.View);
|
||||
const float furthestSliceIndex = GetSliceFromDepth(sphere.ViewSpaceCenter.Z + sphere.Radius, _cache.Data.GridSliceParameters);
|
||||
const float closestSliceIndex = GetSliceFromDepth(sphere.ViewSpaceCenter.Z - sphere.Radius, _cache.Data.GridSliceParameters);
|
||||
sphere.VolumeZBoundsMin = (uint16)Math::Clamp(closestSliceIndex, 0.0f, _cache.GridSize.Z - 1.0f);
|
||||
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) ||
|
||||
sphere.VolumeZBoundsMin > sphere.VolumeZBoundsMax)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context) const
|
||||
{
|
||||
if (renderContext.Buffers->LocalShadowedLightScattering == nullptr)
|
||||
{
|
||||
@@ -251,28 +312,19 @@ GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb2)
|
||||
void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, T& light, PerLight& perLight, GPUConstantBuffer* cb2)
|
||||
{
|
||||
const Float3 center = light.Position;
|
||||
const float radius = light.Radius;
|
||||
ASSERT(!center.IsNanOrInfinity() && !isnan(radius) && !isinf(radius));
|
||||
auto& cache = _cache;
|
||||
|
||||
// Calculate light volume bounds in camera frustum depth range (min and max)
|
||||
const Float3 viewSpaceLightBoundsOrigin = Float3::Transform(center, view.View);
|
||||
const float furthestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z + radius, options, cache.GridSizeZ);
|
||||
const float closestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z - 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);
|
||||
if (volumeZBoundsMin >= volumeZBoundsMax)
|
||||
RasterizeSphere sphere;
|
||||
if (InitSphereRasterize(sphere, view, light.Position, light.Radius))
|
||||
return;
|
||||
auto& cache = _cache;
|
||||
|
||||
// Setup data
|
||||
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
|
||||
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
|
||||
perLight.MinZ = volumeZBoundsMin;
|
||||
perLight.MinZ = sphere.VolumeZBoundsMin;
|
||||
perLight.LocalLightScatteringIntensity = light.VolumetricScatteringIntensity;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(viewSpaceLightBoundsOrigin, radius);
|
||||
perLight.ViewSpaceBoundingSphere = Float4(sphere.ViewSpaceCenter, sphere.Radius);
|
||||
Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip);
|
||||
const bool withShadow = light.CastVolumetricShadow && light.HasShadow;
|
||||
light.SetShaderData(perLight.LocalLight, withShadow);
|
||||
@@ -288,7 +340,7 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
|
||||
// Call rendering to the volume
|
||||
const int32 psIndex = withShadow ? 1 : 0;
|
||||
context->SetState(_psInjectLight.Get(psIndex));
|
||||
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
|
||||
const int32 instanceCount = sphere.VolumeZBoundsMax - sphere.VolumeZBoundsMin + 1;
|
||||
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
|
||||
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
|
||||
context->BindIB(_ibCircleRasterize);
|
||||
@@ -297,9 +349,8 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
|
||||
|
||||
void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
{
|
||||
VolumetricFogOptions options;
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
if (Init(renderContext, context, options))
|
||||
if (Init(renderContext, context))
|
||||
return;
|
||||
auto& view = renderContext.View;
|
||||
auto& cache = _cache;
|
||||
@@ -409,6 +460,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
customData.Shader = _shader->GetShader();
|
||||
customData.GridSize = cache.GridSize;
|
||||
customData.VolumetricFogMaxDistance = cache.Data.VolumetricFogMaxDistance;
|
||||
customData.GridSliceParameters = _cache.Data.GridSliceParameters;
|
||||
bindParams.CustomData = &customData;
|
||||
bindParams.BindViewData();
|
||||
bindParams.DrawCall = renderContext.List->VolumetricFogParticles.begin();
|
||||
@@ -416,19 +468,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
|
||||
for (auto& drawCall : renderContext.List->VolumetricFogParticles)
|
||||
{
|
||||
const Float3 center = drawCall.Particle.VolumetricFog.Position;
|
||||
const float radius = drawCall.Particle.VolumetricFog.Radius;
|
||||
ASSERT(!center.IsNanOrInfinity() && !isnan(radius) && !isinf(radius));
|
||||
|
||||
// Calculate light volume bounds in camera frustum depth range (min and max)
|
||||
const Float3 viewSpaceLightBoundsOrigin = Float3::Transform(center, view.View);
|
||||
const float furthestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z + radius, options, cache.GridSizeZ);
|
||||
const float closestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z - 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 - center).LengthSquared() >= Math::Square(options.Distance + radius) || volumeZBoundsMin >= volumeZBoundsMax)
|
||||
RasterizeSphere sphere;
|
||||
if (InitSphereRasterize(sphere, view, drawCall.Particle.VolumetricFog.Position, drawCall.Particle.VolumetricFog.Radius))
|
||||
continue;
|
||||
|
||||
// Setup material shader data
|
||||
@@ -441,8 +482,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
auto cb2 = _shader->GetShader()->GetCB(2);
|
||||
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
|
||||
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
|
||||
perLight.MinZ = volumeZBoundsMin;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(viewSpaceLightBoundsOrigin, radius);
|
||||
perLight.MinZ = sphere.VolumeZBoundsMin;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(sphere.ViewSpaceCenter, sphere.Radius);
|
||||
Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip);
|
||||
|
||||
// Upload data
|
||||
@@ -450,7 +491,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
context->BindCB(2, cb2);
|
||||
|
||||
// Call rendering to the volume
|
||||
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
|
||||
const int32 instanceCount = sphere.VolumeZBoundsMax - sphere.VolumeZBoundsMin + 1;
|
||||
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
|
||||
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
|
||||
context->BindIB(_ibCircleRasterize);
|
||||
@@ -467,18 +508,19 @@ 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;
|
||||
for (int32 i = 0; i < renderContext.List->PointLights.Count(); i++)
|
||||
{
|
||||
const auto& light = renderContext.List->PointLights.Get()[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance &&
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(options.Distance + light.Radius))
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(distance + light.Radius))
|
||||
pointLights.Add(i);
|
||||
}
|
||||
for (int32 i = 0; i < renderContext.List->SpotLights.Count(); i++)
|
||||
{
|
||||
const auto& light = renderContext.List->SpotLights.Get()[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance &&
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(options.Distance + light.Radius))
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(distance + light.Radius))
|
||||
spotLights.Add(i);
|
||||
}
|
||||
|
||||
@@ -488,7 +530,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
PROFILE_GPU_CPU_NAMED("Lights Injection");
|
||||
|
||||
// Allocate temporary buffer for light scattering injection
|
||||
localShadowedLightScattering = GetLocalShadowedLightScattering(renderContext, context, options);
|
||||
localShadowedLightScattering = GetLocalShadowedLightScattering(renderContext, context);
|
||||
|
||||
// Prepare
|
||||
PerLight perLight;
|
||||
@@ -503,10 +545,14 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
// Render them to the volume
|
||||
context->BindSR(0, shadowMap);
|
||||
context->BindSR(1, shadowsBuffer);
|
||||
auto* pointLightsIdxPtr = pointLights.Get();
|
||||
auto* pointLightsPtr = renderContext.List->PointLights.Get();
|
||||
for (int32 i = 0; i < pointLights.Count(); i++)
|
||||
RenderRadialLight(renderContext, context, view, options, renderContext.List->PointLights[pointLights[i]], perLight, cb2);
|
||||
RenderRadialLight(renderContext, context, view, pointLightsPtr[pointLightsIdxPtr[i]], perLight, cb2);
|
||||
auto* spotLightsIdxPtr = spotLights.Get();
|
||||
auto* spotLightsPtr = renderContext.List->SpotLights.Get();
|
||||
for (int32 i = 0; i < spotLights.Count(); i++)
|
||||
RenderRadialLight(renderContext, context, view, options, renderContext.List->SpotLights[spotLights[i]], perLight, cb2);
|
||||
RenderRadialLight(renderContext, context, view, spotLightsPtr[spotLightsIdxPtr[i]], perLight, cb2);
|
||||
|
||||
// Cleanup
|
||||
context->UnBindCB(2);
|
||||
@@ -557,20 +603,14 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
RenderTargetPool::Release(vBufferB);
|
||||
|
||||
// Update the temporal history buffer
|
||||
if (renderContext.Buffers->VolumetricFogHistory)
|
||||
{
|
||||
RenderTargetPool::Release(renderContext.Buffers->VolumetricFogHistory);
|
||||
}
|
||||
RenderTargetPool::Release(renderContext.Buffers->VolumetricFogHistory);
|
||||
renderContext.Buffers->VolumetricFogHistory = lightScattering;
|
||||
|
||||
// Get buffer for the integrated light scattering (try to reuse the previous frame if it's valid)
|
||||
GPUTexture* integratedLightScattering = renderContext.Buffers->VolumetricFog;
|
||||
if (integratedLightScattering == nullptr || !Float3::NearEqual(integratedLightScattering->Size3(), cache.GridSize))
|
||||
{
|
||||
if (integratedLightScattering)
|
||||
{
|
||||
RenderTargetPool::Release(integratedLightScattering);
|
||||
}
|
||||
RenderTargetPool::Release(integratedLightScattering);
|
||||
integratedLightScattering = RenderTargetPool::Get(volumeDesc);
|
||||
RENDER_TARGET_POOL_SET_NAME(integratedLightScattering, "VolumetricFog.Integrated");
|
||||
renderContext.Buffers->VolumetricFog = integratedLightScattering;
|
||||
@@ -583,10 +623,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
// Final Integration
|
||||
{
|
||||
PROFILE_GPU("Final Integration");
|
||||
|
||||
context->BindUA(0, integratedLightScattering->ViewVolume());
|
||||
context->BindSR(0, lightScattering->ViewVolume());
|
||||
|
||||
context->Dispatch(_csFinalIntegration, groupCountX, groupCountY, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Graphics/GPUPipelineStatePermutations.h"
|
||||
#include "RendererPass.h"
|
||||
#include "DrawCall.h"
|
||||
#include "GI/DynamicDiffuseGlobalIllumination.h"
|
||||
|
||||
struct VolumetricFogOptions;
|
||||
@@ -17,12 +18,12 @@ struct RenderPointLightData;
|
||||
class VolumetricFogPass : public RendererPass<VolumetricFogPass>
|
||||
{
|
||||
public:
|
||||
|
||||
struct CustomData
|
||||
{
|
||||
GPUShader* Shader;
|
||||
Float3 GridSize;
|
||||
float VolumetricFogMaxDistance;
|
||||
Float3 GridSliceParameters;
|
||||
int32 ParticleIndex;
|
||||
};
|
||||
|
||||
@@ -57,6 +58,8 @@ private:
|
||||
float InverseSquaredLightDistanceBiasScale;
|
||||
|
||||
Float4 FogParameters;
|
||||
Float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
|
||||
Matrix PrevWorldToClip;
|
||||
|
||||
@@ -128,6 +131,13 @@ private:
|
||||
/// </summary>
|
||||
Float3 GridSize;
|
||||
|
||||
float SphereRasterizeRadiusBias;
|
||||
|
||||
/// <summary>
|
||||
/// Fog options(from renderer).
|
||||
/// </summary>
|
||||
VolumetricFogOptions Options;
|
||||
|
||||
/// <summary>
|
||||
/// The cached per-frame data for the constant buffer.
|
||||
/// </summary>
|
||||
@@ -137,13 +147,6 @@ private:
|
||||
FrameCache _cache;
|
||||
bool _isSupported;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
VolumetricFogPass();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Renders the volumetric fog (generates integrated light scattering 3D texture). Does nothing if feature is disabled or not supported.
|
||||
@@ -152,12 +155,12 @@ public:
|
||||
void Render(RenderContext& renderContext);
|
||||
|
||||
private:
|
||||
|
||||
bool Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options);
|
||||
GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const;
|
||||
bool Init(RenderContext& renderContext, GPUContext* context);
|
||||
bool InitSphereRasterize(struct RasterizeSphere& sphere, RenderView& view, const Float3& center, float radius);
|
||||
GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context) const;
|
||||
void InitCircleBuffer();
|
||||
template<typename T>
|
||||
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb2);
|
||||
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, T& light, PerLight& perLight, GPUConstantBuffer* cb2);
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
{
|
||||
|
||||
@@ -92,9 +92,4 @@ float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, fl
|
||||
return GetExponentialHeightFog(exponentialHeightFog, posWS, camWS, skipDistance, distance(posWS, camWS));
|
||||
}
|
||||
|
||||
float4 CombineVolumetricFog(float4 fog, float4 volumetricFog)
|
||||
{
|
||||
return float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
#include "./Flax/GBuffer.hlsl"
|
||||
#include "./Flax/VolumetricFog.hlsl"
|
||||
#include "./Flax/ExponentialHeightFog.hlsl"
|
||||
|
||||
// Disable Volumetric Fog if is not supported
|
||||
@@ -21,7 +22,7 @@ DECLARE_GBUFFERDATA_ACCESS(GBuffer)
|
||||
|
||||
Texture2D Depth : register(t0);
|
||||
#if VOLUMETRIC_FOG
|
||||
Texture3D IntegratedLightScattering : register(t1);
|
||||
Texture3D VolumetricFogTexture : register(t1);
|
||||
#endif
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
@@ -34,23 +35,6 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0
|
||||
GBufferData gBufferData = GetGBufferData();
|
||||
float3 viewPos = GetViewPos(gBufferData, input.TexCoord, rawDepth);
|
||||
float3 worldPos = mul(float4(viewPos, 1), gBufferData.InvViewMatrix).xyz;
|
||||
float3 viewVector = worldPos - GBuffer.ViewPos;
|
||||
float sceneDepth = length(viewVector);
|
||||
|
||||
// Calculate volumetric fog coordinates
|
||||
float depthSlice = sceneDepth / ExponentialHeightFog.VolumetricFogMaxDistance;
|
||||
float3 volumeUV = float3(input.TexCoord, depthSlice);
|
||||
|
||||
// Debug code
|
||||
#if VOLUMETRIC_FOG && 0
|
||||
volumeUV = worldPos / 1000;
|
||||
if (!all(volumeUV >= 0 && volumeUV <= 1))
|
||||
return 0;
|
||||
|
||||
return float4(IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0).rgb, 1);
|
||||
//return float4(volumeUV, 1);
|
||||
//return float4(worldPos / 100, 1);
|
||||
#endif
|
||||
|
||||
float skipDistance = 0;
|
||||
#if VOLUMETRIC_FOG
|
||||
@@ -62,7 +46,7 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0
|
||||
|
||||
#if VOLUMETRIC_FOG
|
||||
// Sample volumetric fog and mix it in
|
||||
float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
float4 volumetricFog = SampleVolumetricFog(VolumetricFogTexture, worldPos - GBuffer.ViewPos, ExponentialHeightFog.VolumetricFogMaxDistance, input.TexCoord);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -256,4 +256,9 @@ float3 RotateAboutAxis(float4 normalizedRotationAxisAndAngle, float3 positionOnA
|
||||
return pointOnAxis + rotation - position;
|
||||
}
|
||||
|
||||
float Remap(float value, float fromMin, float fromMax, float toMin, float toMax)
|
||||
{
|
||||
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
40
Source/Shaders/VolumetricFog.hlsl
Normal file
40
Source/Shaders/VolumetricFog.hlsl
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#ifndef __VOLUMETRIC_FOG__
|
||||
#define __VOLUMETRIC_FOG__
|
||||
|
||||
#define VOLUMETRIC_FOG_GRID_Z_LINEAR 1
|
||||
|
||||
float GetDepthFromSlice(float3 gridSliceParameters, float zSlice)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return zSlice * gridSliceParameters.x;
|
||||
#else
|
||||
return (exp2(zSlice / gridSliceParameters.z) - gridSliceParameters.y) / gridSliceParameters.x;
|
||||
#endif
|
||||
}
|
||||
|
||||
float GetSliceFromDepth(float3 gridSliceParameters, float sceneDepth)
|
||||
{
|
||||
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
|
||||
return sceneDepth * gridSliceParameters.y;
|
||||
#else
|
||||
return (log2(sceneDepth * gridSliceParameters.x + gridSliceParameters.y) * gridSliceParameters.z);
|
||||
#endif
|
||||
}
|
||||
|
||||
float4 SampleVolumetricFog(Texture3D volumetricFogTexture, float3 viewVector, float maxDistance, float2 uv)
|
||||
{
|
||||
float sceneDepth = length(viewVector);
|
||||
float zSlice = sceneDepth / maxDistance;
|
||||
// TODO: use GetSliceFromDepth instead to handle non-linear depth distributions
|
||||
float3 volumeUV = float3(uv, zSlice);
|
||||
return volumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
}
|
||||
|
||||
float4 CombineVolumetricFog(float4 fog, float4 volumetricFog)
|
||||
{
|
||||
return float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,9 +17,11 @@
|
||||
#define DEBUG_VOXELS 0
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/Math.hlsl"
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
#include "./Flax/ShadowsSampling.hlsl"
|
||||
#include "./Flax/GBuffer.hlsl"
|
||||
#include "./Flax/VolumetricFog.hlsl"
|
||||
#include "./Flax/GI/DDGI.hlsl"
|
||||
|
||||
struct SkyLightData
|
||||
@@ -50,6 +52,8 @@ float VolumetricFogMaxDistance;
|
||||
float InverseSquaredLightDistanceBiasScale;
|
||||
|
||||
float4 FogParameters;
|
||||
float3 GridSliceParameters;
|
||||
float Dummy1;
|
||||
|
||||
float4x4 PrevWorldToClip;
|
||||
|
||||
@@ -83,23 +87,18 @@ float GetPhase(float g, float cosTheta)
|
||||
return HenyeyGreensteinPhase(g, cosTheta);
|
||||
}
|
||||
|
||||
float GetSliceDepth(float zSlice)
|
||||
{
|
||||
return (zSlice / GridSize.z) * VolumetricFogMaxDistance;
|
||||
}
|
||||
|
||||
float3 GetCellPositionWS(uint3 gridCoordinate, float3 cellOffset, out float sceneDepth)
|
||||
{
|
||||
float2 volumeUV = (gridCoordinate.xy + cellOffset.xy) / GridSize.xy;
|
||||
sceneDepth = GetSliceDepth(gridCoordinate.z + cellOffset.z) / GBuffer.ViewFar;
|
||||
sceneDepth = GetDepthFromSlice(GridSliceParameters, gridCoordinate.z + cellOffset.z) / GBuffer.ViewFar;
|
||||
float deviceDepth = LinearZ2DeviceDepth(GBuffer, sceneDepth);
|
||||
return GetWorldPos(GBuffer, volumeUV, deviceDepth);
|
||||
}
|
||||
|
||||
float3 GetCellPositionWS(uint3 gridCoordinate, float3 cellOffset)
|
||||
{
|
||||
float temp;
|
||||
return GetCellPositionWS(gridCoordinate, cellOffset, temp);
|
||||
float sceneDepth;
|
||||
return GetCellPositionWS(gridCoordinate, cellOffset, sceneDepth);
|
||||
}
|
||||
|
||||
float3 GetVolumeUV(float3 worldPosition, float4x4 worldToClip)
|
||||
@@ -164,17 +163,16 @@ META_PERMUTATION_1(USE_SHADOW=1)
|
||||
float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
|
||||
{
|
||||
uint3 gridCoordinate = uint3(input.Vertex.Position.xy, input.LayerIndex);
|
||||
|
||||
// Prevent from shading locations outside the volume
|
||||
if (!all(gridCoordinate < GridSizeInt))
|
||||
if (any(gridCoordinate >= GridSizeInt))
|
||||
return 0;
|
||||
|
||||
|
||||
// Supersample if history buffer is outside the view
|
||||
float3 historyUV = GetVolumeUV(GetCellPositionWS(gridCoordinate, 0.5f), PrevWorldToClip);
|
||||
float historyAlpha = HistoryWeight;
|
||||
FLATTEN
|
||||
if (any(historyUV < 0) || any(historyUV > 1))
|
||||
historyAlpha = 0;
|
||||
uint samplesCount = historyAlpha < 0.001f ? MissedHistorySamplesCount : 1;
|
||||
uint samplesCount = historyAlpha < 0.01f ? MissedHistorySamplesCount : 1;
|
||||
|
||||
float NoL = 0;
|
||||
bool isSpotLight = LocalLight.SpotAngles.x > -2.0f;
|
||||
@@ -182,8 +180,6 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
|
||||
for (uint sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++)
|
||||
{
|
||||
float3 cellOffset = FrameJitterOffsets[sampleIndex].xyz;
|
||||
//float cellOffset = 0.5f;
|
||||
|
||||
float3 positionWS = GetCellPositionWS(gridCoordinate, cellOffset);
|
||||
float3 cameraVector = normalize(positionWS - GBuffer.ViewPos);
|
||||
float cellRadius = length(positionWS - GetCellPositionWS(gridCoordinate + uint3(1, 1, 1), cellOffset));
|
||||
@@ -202,13 +198,9 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
|
||||
if (attenuation > 0)
|
||||
{
|
||||
if (isSpotLight)
|
||||
{
|
||||
shadow = SampleSpotLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow = SamplePointLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -226,12 +218,10 @@ RWTexture3D<float4> RWVBufferB : register(u1);
|
||||
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
[numthreads(4, 4, 4)]
|
||||
void CS_Initialize(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID)
|
||||
void CS_Initialize(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
uint3 gridCoordinate = DispatchThreadId;
|
||||
|
||||
float voxelOffset = 0.5f;
|
||||
float3 positionWS = GetCellPositionWS(gridCoordinate, voxelOffset);
|
||||
float3 positionWS = GetCellPositionWS(gridCoordinate, 0.5f);
|
||||
|
||||
// Unpack the fog parameters (packing done in C++ ExponentialHeightFog::GetVolumetricFogOptions)
|
||||
float fogDensity = FogParameters.x;
|
||||
@@ -274,26 +264,25 @@ META_CS(true, FEATURE_LEVEL_SM5)
|
||||
META_PERMUTATION_1(USE_DDGI=0)
|
||||
META_PERMUTATION_1(USE_DDGI=1)
|
||||
[numthreads(4, 4, 4)]
|
||||
void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID)
|
||||
void CS_LightScattering(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
uint3 gridCoordinate = DispatchThreadId;
|
||||
float3 lightScattering = 0;
|
||||
uint samplesCount = 1;
|
||||
|
||||
if (any(gridCoordinate >= GridSizeInt))
|
||||
return;
|
||||
|
||||
// Supersample if history buffer is outside the view
|
||||
float3 historyUV = GetVolumeUV(GetCellPositionWS(gridCoordinate, 0.5f), PrevWorldToClip);
|
||||
float historyAlpha = HistoryWeight;
|
||||
FLATTEN
|
||||
if (any(historyUV < 0) || any(historyUV > 1))
|
||||
historyAlpha = 0;
|
||||
samplesCount = historyAlpha < 0.001f && all(gridCoordinate < GridSizeInt) ? MissedHistorySamplesCount : 1;
|
||||
|
||||
uint samplesCount = historyAlpha < 0.01f ? MissedHistorySamplesCount : 1;
|
||||
|
||||
float3 lightScattering = 0;
|
||||
for (uint sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++)
|
||||
{
|
||||
float3 cellOffset = FrameJitterOffsets[sampleIndex].xyz;
|
||||
//float3 cellOffset = 0.5f;
|
||||
|
||||
float sceneDepth;
|
||||
float3 positionWS = GetCellPositionWS(gridCoordinate, cellOffset, sceneDepth);
|
||||
float3 positionWS = GetCellPositionWS(gridCoordinate, cellOffset);
|
||||
float3 cameraVector = positionWS - GBuffer.ViewPos;
|
||||
float cameraVectorLength = length(cameraVector);
|
||||
float3 cameraVectorNormalized = cameraVector / cameraVectorLength;
|
||||
@@ -306,8 +295,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
|
||||
|
||||
#if USE_DDGI
|
||||
// Dynamic Diffuse Global Illumination
|
||||
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 0.0f, cellOffset.x);
|
||||
lightScattering += float4(irradiance, 1);
|
||||
lightScattering += SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 0.0f, cellOffset.x);
|
||||
#else
|
||||
// Sky light
|
||||
if (SkyLight.VolumetricScatteringIntensity > 0)
|
||||
@@ -334,12 +322,9 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
|
||||
float4 historyScatteringAndExtinction = LightScatteringHistory.SampleLevel(SamplerLinearClamp, historyUV, 0);
|
||||
scatteringAndExtinction = lerp(scatteringAndExtinction, historyScatteringAndExtinction, historyAlpha);
|
||||
}
|
||||
|
||||
if (all(gridCoordinate < GridSizeInt))
|
||||
{
|
||||
scatteringAndExtinction = select(or(isnan(scatteringAndExtinction), isinf(scatteringAndExtinction)), 0, scatteringAndExtinction);
|
||||
RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0);
|
||||
}
|
||||
|
||||
scatteringAndExtinction = select(or(isnan(scatteringAndExtinction), isinf(scatteringAndExtinction)), 0, scatteringAndExtinction);
|
||||
RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0);
|
||||
}
|
||||
|
||||
#elif defined(_CS_FinalIntegration)
|
||||
@@ -350,9 +335,11 @@ Texture3D<float4> LightScattering : register(t0);
|
||||
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
[numthreads(8, 8, 1)]
|
||||
void CS_FinalIntegration(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID)
|
||||
void CS_FinalIntegration(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
uint3 gridCoordinate = DispatchThreadId;
|
||||
if (any(gridCoordinate.xy >= GridSizeInt.xy))
|
||||
return;
|
||||
float4 acc = float4(0, 0, 0, 1);
|
||||
float3 prevPositionWS = GBuffer.ViewPos;
|
||||
|
||||
@@ -363,11 +350,17 @@ void CS_FinalIntegration(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV
|
||||
float3 positionWS = GetCellPositionWS(coords, 0.5f);
|
||||
|
||||
// Ref: "Physically Based and Unified Volumetric Rendering in Frostbite"
|
||||
float transmittance = exp(-scatteringExtinction.w * length(positionWS - prevPositionWS));
|
||||
float stepDistance = length(positionWS - prevPositionWS);
|
||||
float transmittance = exp(-scatteringExtinction.w * stepDistance);
|
||||
float3 scatteringIntegratedOverSlice = (scatteringExtinction.rgb - scatteringExtinction.rgb * transmittance) / max(scatteringExtinction.w, 0.00001f);
|
||||
|
||||
// Apply distance fade
|
||||
float distanceFade = Remap(layerIndex, GridSizeInt.z * 0.8f, GridSizeInt.z - 1, 1, 0);
|
||||
scatteringIntegratedOverSlice *= distanceFade;
|
||||
|
||||
// Accumulate
|
||||
acc.rgb += scatteringIntegratedOverSlice * acc.a;
|
||||
acc.a *= transmittance;
|
||||
|
||||
#if DEBUG_VOXELS
|
||||
RWIntegratedLightScattering[coords] = float4(scatteringExtinction.rgb, 1.0f);
|
||||
#elif DEBUG_VOXEL_WS_POS
|
||||
|
||||
Reference in New Issue
Block a user