Files
UnrealEngine/Engine/Shaders/Private/DiaphragmDOF/DOFGatherAccumulator.ush
2025-05-18 13:04:45 +08:00

832 lines
29 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DiaphragmDOF/DOFGatherAccumulator.usf: gather accumulator for diaphragm DOF.
=============================================================================*/
#pragma once
#include "DOFGatherCommon.ush"
//------------------------------------------------------- GATHER ACCUMULATOR
/** Accumulator of all gathered samples. */
struct FGatherAccumulator
{
// Gather parameters.
FGatherInputParameters GatherParameters;
// Gathering kernel for larger.
FLargerConvolution LargerConvolution;
// Layer to process.
uint LayerProcessing;
#if GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_SIMPLE
// One single layer.
float4 Color;
float ColorWeight;
float Opacity;
float OpacityWeight;
// Distance to the closest foreground sample.
float DistanceToClosestForeground;
#elif GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_JIMENEZ2014
// Two layers, 0 is the closest to camera.
// Hole filling?
bool bIsHoleFillingForForeground;
// Coc radius of the center of the kernel.
float CenterCocRadius;
// Scene color per layer.
float4 Layer[2];
// Weight of Layer.
float LayerWeight[2];
// Number of samples accumulated in each layers.
float LayerSampleCount[2];
float IntersectionSum;
float ForegroundSum;
#elif GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_RINGBINNING
// Accumulated results for background reconstruction.
// float4 BackgroundReconstruction; // TODO.
// Accumulated results for previous bucket.
float4 PreviousColor;
float PreviousCocRadius;
float PreviousCocRadiusSquare;
float PreviousWeight;
// Accumulating results for current bucket.
float4 CurrentColor;
float CurrentCocRadius;
float CurrentCocRadiusSquare;
float CurrentWeight;
float CurrentTranslucency;
// The bordering radius between current and previous buckets.
float BorderingRadius;
bool bIsFirstRing;
#else
#error Unknown gather accumulator.
#endif
#if CONFIG_LAYER_GATHERING
float FarBorderingRadius;
float CloseBorderingRadius;
#endif
};
/** Create an accumulator for gathering. */
FGatherAccumulator CreateGatherAccumulator(
in FGatherInputParameters GatherParameters,
in FLargerConvolution LargerConvolution)
{
FGatherAccumulator Accumulator;
Accumulator.GatherParameters = GatherParameters;
// Always store larger convolution in accumulator, in case want to compose larger convolution
// in ResolveForegroundAndBackgroundOutputs().
Accumulator.LargerConvolution = LargerConvolution;
Accumulator.LayerProcessing = DIM_LAYER_PROCESSING;
#if GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_JIMENEZ2014
{
Accumulator.IntersectionSum = 0;
Accumulator.ForegroundSum = 0;
Accumulator.bIsHoleFillingForForeground = false;
Accumulator.CenterCocRadius = 0.0;
for (uint k = 0; k < 2; k++)
{
Accumulator.Layer[k] = 0;
Accumulator.LayerWeight[k] = 0;
Accumulator.LayerSampleCount[k] = 0;
}
}
#elif GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_RINGBINNING
{
Accumulator.PreviousColor = 0;
Accumulator.PreviousCocRadius = 0;
Accumulator.PreviousCocRadiusSquare = 0;
Accumulator.PreviousWeight = 0;
Accumulator.CurrentColor = 0;
Accumulator.CurrentCocRadius = 0;
Accumulator.CurrentCocRadiusSquare = 0;
Accumulator.CurrentWeight = 0;
Accumulator.CurrentTranslucency = 0;
}
#elif GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_SIMPLE
{
Accumulator.Color = 0;
Accumulator.ColorWeight = 0;
Accumulator.Opacity = 0;
Accumulator.OpacityWeight = 0;
Accumulator.DistanceToClosestForeground = EXTREMELY_LARGE_COC_RADIUS;
}
#endif
#if CONFIG_LAYER_GATHERING
{
Accumulator.FarBorderingRadius = 0;
Accumulator.CloseBorderingRadius = 0;
}
#endif
return Accumulator;
}
//------------------------------------------------------- Simple standalone bin accumulator.
#if GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_SIMPLE
struct FGatherSampleDerivedParameters
{
float Weight;
float IsConsidered;
float Opacity;
};
// Returns the opacity to use to transition to background.
float ComputeBackgroundSampleOpacity(in const FGatherInputParameters GatherParameters, float CocRadius)
{
//return CocRadius < GatherParameters.MaxRecombineAbsCocRadius ? 1 : 0;
return saturate(GatherParameters.MaxRecombineAbsCocRadius - CocRadius);
}
FGatherSampleDerivedParameters ComputeSampleDerivates(in FGatherAccumulator Accumulator, in FGatherSample A)
{
FGatherSampleDerivedParameters DerivedA;
DerivedA.Weight = ComputeSampleWeight(A.CocRadius);
//if (Accumulator.LayerProcessing == LAYER_PROCESSING_FOREGROUND_ONLY)
//{
// DerivedA.Opacity = ComputeForegroundSampleOpacity(A.CocRadius);
// DerivedA.IsConsidered = IsConsideredForegroundSample(A.CocRadius);
//}
if (Accumulator.LayerProcessing == LAYER_PROCESSING_FOREGROUND_HOLE_FILLING)
{
// Don't care about weight for hole filling
DerivedA.Weight = 1;
DerivedA.Opacity = 1;
//DerivedA.Opacity = ComputeForegroundSampleOpacity(A.CocRadius);
DerivedA.IsConsidered = IsConsideredCocRadius(Accumulator.GatherParameters, A.CocRadius);
}
else if (Accumulator.LayerProcessing == LAYER_PROCESSING_SLIGHT_OUT_OF_FOCUS)
{
DerivedA.Opacity = ComputeBackgroundSampleOpacity(Accumulator.GatherParameters, A.CocRadius);
DerivedA.IsConsidered = saturate(Accumulator.GatherParameters.MaxRecombineAbsCocRadius - abs(A.CocRadius));
}
//else if (Accumulator.LayerProcessing == LAYER_PROCESSING_BACKGROUND_ONLY)
//{
// DerivedA.Opacity = ComputeBackgroundSampleOpacity(A.CocRadius);
// DerivedA.IsConsidered = IsConsideredBackgroundSample(A.CocRadius);
//}
return DerivedA;
}
void HoleFillCloserSample(
in FGatherAccumulator Accumulator,
inout FGatherSample A, inout FGatherSampleDerivedParameters DerivedA,
in FGatherSample Closer, in FGatherSampleDerivedParameters DerivedCloser)
{
if (Accumulator.LayerProcessing == LAYER_PROCESSING_FOREGROUND_ONLY)
{
A.Intersection = Closer.Intersection;
DerivedA.Weight = DerivedCloser.Weight;
#if 1 // Used with LAYER_PROCESSING_FOREGROUND_HOLE_FILLING
#elif 0 // looks nice over slight out of focus, but looks bad over large background out of focus.
Opacity[1] = Opacity[0] * ComputeBackgroundSampleOpacity(Accumulator.GatherParameters, S[1].CocRadius);
IsConsidered[1] = Opacity[1] * IsConsidered[0];
#else
DerivedA.IsConsidered = DerivedCloser.IsConsidered;
DerivedA.Opacity = DerivedCloser.Opacity;
#endif
}
else if (Accumulator.LayerProcessing == LAYER_PROCESSING_FOREGROUND_HOLE_FILLING)
{
A.Intersection = Closer.Intersection;
DerivedA.Weight = DerivedCloser.Weight;
DerivedA.Opacity = DerivedCloser.Opacity;
}
else if (Accumulator.LayerProcessing == LAYER_PROCESSING_SLIGHT_OUT_OF_FOCUS)
{
A.Intersection = Closer.Intersection;
DerivedA.Weight = DerivedCloser.Weight;
}
}
/** Accumulates sample. */
void AccumulateSample(
inout FGatherAccumulator Accumulator,
in FGatherSample A,
in FGatherSampleDerivedParameters DerivedA)
{
// TODO: soften that.
if (Accumulator.LayerProcessing == LAYER_PROCESSING_FOREGROUND_HOLE_FILLING &&
A.CocRadius < -(Accumulator.GatherParameters.MaxRecombineAbsCocRadius - 1))
{
Accumulator.DistanceToClosestForeground = min(Accumulator.DistanceToClosestForeground, A.Distance);
}
else
{
float ColorWeight = A.Intersection * DerivedA.Weight * DerivedA.IsConsidered;
float OpacityWeight = A.Intersection;
Accumulator.Color += A.Color * ColorWeight;
Accumulator.ColorWeight += ColorWeight;
Accumulator.Opacity += OpacityWeight * DerivedA.Opacity;
Accumulator.OpacityWeight += OpacityWeight;
}
}
/** Accumulates center sample. */
void AccumulateCenterSample(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
FGatherSampleDerivedParameters DerivedA = ComputeSampleDerivates(Accumulator, A);
AccumulateSample(Accumulator, A, DerivedA);
}
void AccumulateCenterSampleAsItsOwnRing(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
AccumulateCenterSample(/* inout */ Accumulator, A);
}
/** Changes the already accumulated weight, used when doing varying gathering sample density. */
void AmendAccumulatedRingsWeight(
inout FGatherAccumulator Accumulator,
in FGatherInputParameters NewGatherParameters,
float AccumulatedWeightMultiplier)
{
// TODO: UNIMPLEMENTED
}
/** Setup up the accumulator for a ring. */
void StartRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
// NOP
}
/** Accumulates mirror samples. */
void AccumulateMirrorSamples(inout FGatherAccumulator Accumulator, in FGatherSample A, in FGatherSample B)
{
FGatherSampleDerivedParameters DerivedA = ComputeSampleDerivates(Accumulator, A);
FGatherSampleDerivedParameters DerivedB = ComputeSampleDerivates(Accumulator, B);
// Hole filling for foreground pixels.
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_HOLE_FILLING
{
if (IsForeground(A.CocRadius) && B.CocRadius > A.CocRadius)
{
B.Intersection = max(B.Intersection, A.Intersection);
}
else if (IsForeground(B.CocRadius) && A.CocRadius > B.CocRadius)
{
A.Intersection = max(B.Intersection, A.Intersection);
}
}
#elif DIM_LAYER_PROCESSING == LAYER_PROCESSING_SLIGHT_OUT_OF_FOCUS
{
if (B.CocRadius > A.CocRadius)
{
B.Intersection = A.Intersection;
DerivedB.Weight = DerivedA.Weight;
}
else
{
A.Intersection = B.Intersection;
DerivedA.Weight = DerivedB.Weight;
}
}
#else
#error Unimplemented hole filling.
#endif
AccumulateSample(Accumulator, A, DerivedA);
AccumulateSample(Accumulator, B, DerivedB);
}
/** Digests all samples accumulated for the ring. */
void EndRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
// NOP
}
/** Resolves the background layer output from the accumulator. */
void ResolveAccumulatorOutputs(
in FGatherAccumulator Accumulator,
in FGatherResolveInfos GatherResolveInfos,
out FAccumulatorOutput Output)
{
// Initializes Output.
Output = CreateAccumulatorOutput();
float4 NormalizedColor = Accumulator.Color * SafeRcp(Accumulator.ColorWeight);
float NormalizedOpacity = Accumulator.Opacity * SafeRcp(Accumulator.OpacityWeight);
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_HOLE_FILLING
if (Accumulator.DistanceToClosestForeground != EXTREMELY_LARGE_COC_RADIUS && Accumulator.ColorWeight > 0)
{
float Translucency = Accumulator.DistanceToClosestForeground / Accumulator.GatherParameters.KernelRadius;
float Opacity = 1 - Translucency;
Output.ForegroundHoleFillingColor = NormalizedColor * Opacity;
Output.ForegroundHoleFillingAlpha = Translucency;
}
#elif DIM_LAYER_PROCESSING == LAYER_PROCESSING_SLIGHT_OUT_OF_FOCUS
{
Output.SlightFocusColor = NormalizedColor;
Output.SlightFocusOpacity = NormalizedOpacity;
}
#else
#error Unsupported.
#endif
}
#endif
//------------------------------------------------------- JIMENEZ 2014: CALL OF DUTY WAREFARE.
#if GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_JIMENEZ2014
/** Returns the layer id the sample should go in. (Jiminez 2014, slide 100) */
float ComputeSampleLayer(in FGatherAccumulator Accumulator, in FGatherSample A)
{
const float CocLayerTransition = 0.5;
const float CocLayer0Thickness = 0.0;
float LayerId = saturate((A.CocRadius - Accumulator.GatherParameters.ClosestCocRadius - CocLayer0Thickness) * CocLayerTransition);
#if CONFIG_ACCUMULATOR_FULL_ALU
return smoothstep(0.0, 1.0, LayerId);
#else
return LayerId;
#endif
}
float ComputeForegroundFading(in const FGatherInputParameters GatherParameters, float CocRadius)
{
return saturate((-CocRadius) - (GatherParameters.MaxRecombineAbsCocRadius - 1.0));
}
/** Accumulates a sample. */
void AccumulateSample(inout FGatherAccumulator Accumulator, in FGatherSample A, float Weight)
{
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_ONLY
if (!IsForeground(A.CocRadius))
{
A.Intersection = 0.0;
}
A.Intersection *= saturate((-A.CocRadius) - (Accumulator.GatherParameters.MaxRecombineAbsCocRadius - 1.0));
#elif DIM_LAYER_PROCESSING == LAYER_PROCESSING_BACKGROUND_ONLY
if (IsForeground(A.CocRadius))
{
A.Intersection = 0.0;
}
else if (Accumulator.bIsHoleFillingForForeground)
{
A.Intersection = 1.0;
}
#endif
//A.Intersection *= IsConsideredCocRadius(A.CocRadius);
float IsInLayer1 = ComputeSampleLayer(Accumulator, A);
float IsInLayer0 = 1.0 - IsInLayer1;
Accumulator.Layer[1] += A.Color * (IsInLayer1 * A.Intersection * Weight);
Accumulator.LayerWeight[1] += IsInLayer1 * A.Intersection * Weight;
Accumulator.LayerSampleCount[1] += IsInLayer1;
Accumulator.Layer[0] += A.Color * (IsInLayer0 * A.Intersection * Weight);
Accumulator.LayerWeight[0] += IsInLayer0 * A.Intersection * Weight;
Accumulator.LayerSampleCount[0] += IsInLayer0;
Accumulator.IntersectionSum += A.Intersection;
Accumulator.ForegroundSum += (IsForeground(A.CocRadius) ? 1.0 : 0.0) * saturate((-A.CocRadius) - (Accumulator.GatherParameters.MaxRecombineAbsCocRadius - 1.0));
}
/** Accumulates center sample. */
void AccumulateCenterSample(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
// Fade intersection as CocRadius becomes close to 0 to fallback to full resolution gathering.
A.Intersection *= ComputeForegroundFading(Accumulator.GatherParameters, A.CocRadius);
// TODO: when accumulating the foreground, there is an issue in geometric edges.
#if 1
float WeightA = ComputeSampleWeight(A.CocRadius);
AccumulateSample(Accumulator, A, WeightA);
Accumulator.bIsHoleFillingForForeground = IsForeground(A.CocRadius);
Accumulator.CenterCocRadius = A.CocRadius;
#endif
}
void AccumulateCenterSampleAsItsOwnRing(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
AccumulateCenterSample(/* inout */ Accumulator, A);
}
/** Changes the already accumulated weight, used when doing varying gathering sample density. */
void AmendAccumulatedRingsWeight(
inout FGatherAccumulator Accumulator,
in FGatherInputParameters NewGatherParameters,
float AccumulatedWeightMultiplier)
{
// TODO: UNIMPLEMENTED
}
void StartRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
// NOP.
}
/** Accumulates mirror samples. */
void AccumulateMirrorSamples(inout FGatherAccumulator Accumulator, in FGatherSample A, in FGatherSample B)
{
float WeightA = ComputeSampleWeight(A.CocRadius);
float WeightB = ComputeSampleWeight(B.CocRadius);
// Jimenez 2014, slide 70: sample mirroring.
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_ONLY
{
// Fade intersection as CocRadius becomes close to 0 to fallback to full resolution gathering.
A.Intersection *= ComputeForegroundFading(Accumulator.GatherParameters, A.CocRadius);
B.Intersection *= ComputeForegroundFading(Accumulator.GatherParameters, B.CocRadius);
// If sample B is behind sample A. CocRadius < 0 when foreground.
if (B.CocRadius > A.CocRadius)
{
B.Intersection = A.Intersection;
B.CocRadius = A.CocRadius;
WeightB = WeightA;
}
else
{
A.Intersection = B.Intersection;
A.CocRadius = B.CocRadius;
WeightA = WeightB;
}
}
#endif
AccumulateSample(Accumulator, A, WeightA);
AccumulateSample(Accumulator, B, WeightB);
}
void EndRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
// NOP.
}
/** Resolves the foreground layer output from the accumulator. */
void ResolveAccumulatorOutputs(
in FGatherAccumulator Accumulator,
in FGatherResolveInfos GatherResolveInfos,
out FAccumulatorOutput Output)
{
// Initializes Output.
Output = CreateAccumulatorOutput();
// Total number of sample done.
float TotalSampleCount = GatherResolveInfos.SamplesCount;
// Compute opacity of layer 0.
// Jimenez 2014, slide 102.
// Math verified by Guillaume.
float KernelWeight = ComputeSampleWeight(Accumulator.GatherParameters.KernelRadius * ((Accumulator.GatherParameters.RingCount + 0.5) / Accumulator.GatherParameters.RingCount));
float KernelArea = rcp(KernelWeight);
float Layer0Opacity = saturate(
(Accumulator.LayerWeight[1] == 0 ? 1 : 0) +
((1.0 / float(TotalSampleCount)) * KernelArea * Accumulator.LayerWeight[0]));
#if 1
Output.ForegroundColor = lerp(
Accumulator.Layer[1] * SafeRcp(Accumulator.LayerWeight[1]),
Accumulator.Layer[0] * SafeRcp(Accumulator.LayerWeight[0]),
Layer0Opacity);
#else
Output.ForegroundColor = (Accumulator.Layer[0] + Accumulator.Layer[1]) * SafeRcp(Accumulator.LayerWeight[0] + Accumulator.LayerWeight[1]);
#endif
#if 0
Output.ForegroundAlpha = saturate(Accumulator.IntersectionSum / float(TotalSampleCount));
#else
// The most reliable way to get a proper alpha channel for foreground is to just count the number of foreground pixel we have found,
// with the gather, regardless of whether their coc has intersected with the output pixel.
Output.ForegroundAlpha = Accumulator.ForegroundSum * rcp(float(TotalSampleCount));
// But it is then technically possible to have found foreground sample, but intersected none of them. Therefor, to avoid
// dark layout arround out of focus, we force the alpha to be 0 in that specific case.
Output.ForegroundAlpha = (Accumulator.LayerWeight[0] + Accumulator.LayerWeight[1]) > 0.0 ? Output.ForegroundAlpha : 0.0;
#endif
// This are same implementation, and LAYER_PROCESSING_FOREGROUND_AND_BACKGROUND is not supported.
Output.BackgroundColor = Output.ForegroundColor;
// To fill with full resolution.
// HACK: return the sum of the weight because only used for testing > 0.
Output.BackgroundWeight = Accumulator.LayerWeight[0] + Accumulator.LayerWeight[1];
}
#endif // GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_JIMENEZ2014
//------------------------------------------------------- RING BINNING ACCUMULATOR.
/**
* Unpublished in house made depth sorting accumulator for background out of focus layer.
*
* It is based on the fundamental fact that far rings of the kernel will only intersect as large or larger
* CocRadius than distance of the ring from the center of ther kernel.
*
* The idea is to accumulate the kernel, starting from the largest ring N, to intersect with closer geometry
* that have a smaller Coc at the end of the accumulation.
*
* Considering the samples of ring I < N, there is two buckets:
* * previous bucket: that is the accumulation of all samples that have larger Coc than current ring I's
* radius, that were found on previously accumulated rings [[N; I[[;
* * current bucket: that is the accumulation of all samples that have arround the same Coc as current ring's radius.
* Note that it is impossible to find significantly smaller CocRadius in this bucket, because such input sample
* would not intersect with the center of the gathering kernel on ring I.
*
* For every sample of the ring, it is categorized whether it belongs to previous bucket or current bucket by simply
* comparing its Coc radius against the average radius of I and I + 1, known as the bucket bordering radius.
*
* (O = center of the kernel) (A = a sample of ring I) (B = a sample of ring I + 1)
* |
* O ----[...]---- A K B
* |
* (border between current and previous buckets for ring I)
*
* distance(O, K) == the bucket bordering radius.
*
* Once all the sample of the ring I has been accumulated, the current bucket get composited into the previous bucket,
* and get reset to accumulate the next ring I-1, and the bucket bordering radius get updated as well to be between
* ring I and ring I - 1.
*
* The compositing of the current bucket into the previous bucket is simply an hybrid compositing operator between
* an additive and opacity based blending, that takes into account the following inputs:
* * the average Coc radius of the current bucket;
* * the average Coc radius of the previous bucket;
* * the translucency of the current bucket.
*
* The idea of this compositing operator is simply to do additive blending when average Coc radii are same, or
* do opacity based compositing otherwise.
*
*
*/
#if GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_RINGBINNING
// Because largest rings while intersect only with far away pixels, we processed them first so that lower ring get
// Blended on top if they are closer.
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_BACKGROUND_ONLY
#define LARGEST_RINGS_FIRST 1
#elif DIM_LAYER_PROCESSING == LAYER_PROCESSING_FOREGROUND_ONLY
#define LARGEST_RINGS_FIRST 0
#error Fix me.
#else
#error Fix me.
#endif
/** Accumulates sample. */
void AccumulateSample(inout FGatherAccumulator Accumulator, in FGatherSample A, float Weight)
{
#if DIM_LAYER_PROCESSING == LAYER_PROCESSING_BACKGROUND_ONLY
if (IsForeground(A.CocRadius))
{
A.Intersection = 0.0;
}
// TODO.
//else if (Accumulator.bIsHoleFillingForForeground)
//{
// A.Intersection = 1.0;
//}
#endif
// Fade intersection if sample's Coc radius is out of the gathering pass coc bounds.
float IsConsidered = IsConsideredCocRadius(Accumulator.GatherParameters, A.CocRadius);
// Compare the sample's Coc radius with the bucket bordering radius.
float bBelongsToPrevious = saturate(A.CocRadius - Accumulator.BorderingRadius + 0.5);
#if CONFIG_ACCUMULATOR_FULL_ALU
bBelongsToPrevious = smoothstep(0.0, 1.0, bBelongsToPrevious);
#endif
// First ring is directly accumulated to the current bucket, because merge with potential larger .
if (Accumulator.bIsFirstRing)
{
bBelongsToPrevious = 0.0;
}
float bBelongsToCurrent = 1.0 - bBelongsToPrevious;
#if CONFIG_LAYER_GATHERING
if (!Accumulator.bIsFirstRing)
{
float M = 1;
M *= 1 - saturate(A.CocRadius - Accumulator.FarBorderingRadius + 0.5);
M *= saturate(A.CocRadius - Accumulator.CloseBorderingRadius + 0.5);
bBelongsToCurrent *= saturate(A.CocRadius - Accumulator.CloseBorderingRadius + 0.5);
bBelongsToPrevious *= 1 - saturate(A.CocRadius - Accumulator.FarBorderingRadius + 0.5);
}
#endif
float CurrentWeight = (A.Intersection * IsConsidered * Weight) * bBelongsToCurrent;
Accumulator.CurrentColor += A.Color * CurrentWeight;
Accumulator.CurrentCocRadius += A.CocRadius * CurrentWeight;
Accumulator.CurrentCocRadiusSquare += A.CocRadius * (A.CocRadius * CurrentWeight);
Accumulator.CurrentWeight += CurrentWeight;
float PreviousWeight = (A.Intersection * IsConsidered * Weight) * bBelongsToPrevious;
Accumulator.PreviousColor += A.Color * PreviousWeight;
Accumulator.PreviousCocRadius += A.CocRadius * PreviousWeight;
Accumulator.PreviousCocRadiusSquare += A.CocRadius * (A.CocRadius * PreviousWeight);
Accumulator.PreviousWeight += PreviousWeight;
// Accumulate current bucket translucency.
{
float SampleTranslucency = saturate(A.CocRadius - Accumulator.BorderingRadius);
//Accumulator.CurrentTranslucency += A.CocRadius > Accumulator.BorderingRadius;
Accumulator.CurrentTranslucency += SampleTranslucency;
}
}
/** Merge current bucket into the previous bucket. */
void MergeCurrentBucketIntoPreviousBucket(inout FGatherAccumulator Accumulator, float SampleCount)
{
// If have not accumulated any sample into the current bucket, just avoid this useless work and the
// NaNs that would be coming with.
if (Accumulator.CurrentWeight == 0.0)
{
return;
}
// Opacity of the current bucket.
float CurrentRingOpacity = saturate(1 - Accumulator.CurrentTranslucency * rcp(SampleCount));
// Compute current and previous Coc radii.
float PreviousCocRadius = Accumulator.PreviousCocRadius * rcp(Accumulator.PreviousWeight);
float CurrentCocRadius = Accumulator.CurrentCocRadius * rcp(Accumulator.CurrentWeight);
// Whether current bucket is occluding previous bucket.
// TODO: put a contrast.
float bOccludingCoc = saturate((PreviousCocRadius - CurrentCocRadius));
// Compute final factor to use to with previous bucket.
float PreviousBucketFactor = (Accumulator.PreviousWeight == 0.0) ? 0.0 : (1.0 - CurrentRingOpacity * bOccludingCoc);
// Compose current ring into previous.
Accumulator.PreviousColor = Accumulator.PreviousColor * PreviousBucketFactor + Accumulator.CurrentColor;
Accumulator.PreviousCocRadius = Accumulator.PreviousCocRadius * PreviousBucketFactor + Accumulator.CurrentCocRadius;
Accumulator.PreviousCocRadiusSquare = Accumulator.PreviousCocRadiusSquare * PreviousBucketFactor + Accumulator.CurrentCocRadiusSquare;
Accumulator.PreviousWeight = Accumulator.PreviousWeight * PreviousBucketFactor + Accumulator.CurrentWeight;
}
/** Like mirror samples. */
void AccumulateCenterSample(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
float WeightA = ComputeSampleWeight(A.CocRadius);
AccumulateSample(Accumulator, A, WeightA);
}
/** Accumulates center sample as it s own ring potentially occluding previous ring instead of between StartRingSamples() and EndRingSamples(). */
void AccumulateCenterSampleAsItsOwnRing(inout FGatherAccumulator Accumulator, in FGatherSample A)
{
// Error in coc radius error introduced by the under sampling of the kernel
const float CocRadiusError = CONFIG_COC_RADIUS_CORRECTION ? 1.0 : 0.0;
// Set the bordering radius at exactly the radius of the center sample.
Accumulator.BorderingRadius = (0.5 + CocRadiusError) * Accumulator.GatherParameters.KernelRadius * rcp(0.5 + Accumulator.GatherParameters.RingCount);
// Center sample is first ring only when smallest ring first.
Accumulator.bIsFirstRing = LARGEST_RINGS_FIRST == 0;
// Accumulate the center's sample.
AccumulateCenterSample(Accumulator, A);
MergeCurrentBucketIntoPreviousBucket(Accumulator, 1.0);
}
/** Changes the already accumulated weight, used when doing varying gathering sample density. */
void AmendAccumulatedRingsWeight(
inout FGatherAccumulator Accumulator,
in FGatherInputParameters NewGatherParameters,
float AccumulatedWeightMultiplier)
{
Accumulator.GatherParameters = NewGatherParameters;
Accumulator.PreviousColor *= AccumulatedWeightMultiplier;
Accumulator.PreviousCocRadius *= AccumulatedWeightMultiplier;
Accumulator.PreviousCocRadiusSquare *= AccumulatedWeightMultiplier;
Accumulator.PreviousWeight *= AccumulatedWeightMultiplier;
}
/** Setup up the accumulator for a ring. */
void StartRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
Accumulator.bIsFirstRing = RingInfos.bIsFirstRing;
// Error in coc radius error introduced by the under sampling of the kernel
const float CocRadiusError = CONFIG_COC_RADIUS_CORRECTION ? 1.0 : 0.0;
Accumulator.BorderingRadius = (RingInfos.RingId + (1.5 + CocRadiusError)) * (Accumulator.GatherParameters.KernelRadius * rcp(0.5 + Accumulator.GatherParameters.RingCount));
#if CONFIG_SGPR_HINTS
{
Accumulator.BorderingRadius = ToScalarMemory(Accumulator.BorderingRadius);
}
#endif
}
/** Accumulates mirror samples. */
void AccumulateMirrorSamples(inout FGatherAccumulator Accumulator, in FGatherSample A, in FGatherSample B)
{
// TODO: mirroring hole filling like Jimenez 2014 on slide 70? Don't think this necessary as it is
// semantically usefull only for foreground.
float WeightA = ComputeSampleWeight(A.CocRadius);
float WeightB = ComputeSampleWeight(B.CocRadius);
AccumulateSample(Accumulator, A, WeightA);
AccumulateSample(Accumulator, B, WeightB);
}
/** Digests all samples accumulated for the ring. */
void EndRingSamples(inout FGatherAccumulator Accumulator, in FGatherRingInfos RingInfos)
{
if (static_condition(RingInfos.bIsFirstRing)) // static_condition because number of ring for loop have UNROLL.
{
// Simply move current bucket into previous bucket.
Accumulator.PreviousColor = Accumulator.CurrentColor;
Accumulator.PreviousCocRadius = Accumulator.CurrentCocRadius;
Accumulator.PreviousCocRadiusSquare = Accumulator.CurrentCocRadiusSquare;
Accumulator.PreviousWeight = Accumulator.CurrentWeight;
}
else
{
MergeCurrentBucketIntoPreviousBucket(Accumulator, RingInfos.SampleCount);
}
// Reset current bucket accumulators.
Accumulator.CurrentColor = 0;
Accumulator.CurrentCocRadius = 0;
Accumulator.CurrentCocRadiusSquare = 0;
Accumulator.CurrentWeight = 0;
Accumulator.CurrentTranslucency = 0;
}
/** Resolves the background layer output from the accumulator. */
void ResolveAccumulatorOutputs(
in FGatherAccumulator Accumulator,
in FGatherResolveInfos GatherResolveInfos,
out FAccumulatorOutput Output)
{
// Initializes Output.
Output = CreateAccumulatorOutput();
float InvWeight = SafeRcp(Accumulator.PreviousWeight);
// There is a MergeCurrentIntoPrevious() in the center sample, so just need to return normalized
// previous bucket's color and weight.
Output.BackgroundColor = Accumulator.PreviousColor * InvWeight;
Output.BackgroundWeight = Accumulator.PreviousWeight;
Output.BackgroundCocAvgAndSquareAvg.x = Accumulator.PreviousCocRadius * InvWeight;
Output.BackgroundCocAvgAndSquareAvg.y = Accumulator.PreviousCocRadiusSquare * InvWeight;
}
#endif // GATHER_ACCUMULATOR_METHOD == GATHER_ACCUMULATOR_METHOD_RINGBINNING