832 lines
29 KiB
HLSL
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
|