Skip to content

Commit

Permalink
[Fogbugz 1375978, 1413534] Fixed pb dof artifacts
Browse files Browse the repository at this point in the history
Fix DoF artifacts when the physically based mode is used. 
https://fogbugz.unity3d.com/f/cases/1375978/
https://fogbugz.unity3d.com/f/cases/1413534/

Some previous tiling optimizations left the pb dof mode in a bad state. This PR fixes many small issues in the shaders:
- Fixed "sticky" CoC reprojection when TAA and pb DoF is enabled
- Fixed tiling artifacts from early exit in low res dof pass
- Fixed (more) tiling artifacts by disabling the per-tile layer classification 
(the difference in sampling was visible in neightboring tiles)
- Fixed a corner case with background blending

Before:
![image](https://media.github.cds.internal.unity3d.com/user/1822/files/deabc55a-83ad-4237-9fc5-f99bda9a7c35)

After:
![image](https://media.github.cds.internal.unity3d.com/user/1822/files/b93dd569-39bd-4b46-9eb3-63006d533a2c)
  • Loading branch information
pmavridis committed Jun 22, 2022
1 parent 6c4197e commit 3b27c69
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ public DepthOfFieldResolution resolution
DepthOfFieldResolutionParameter m_Resolution = new DepthOfFieldResolutionParameter(DepthOfFieldResolution.Half);

[AdditionalProperty]
[Tooltip("When enabled, HDRP uses bicubic filtering instead of bilinear filtering for the depth of field effect.")]
[Tooltip("When enabled, HDRP uses bicubic instead of bilinear filtering for the depth of field effect. Also conceals tiling artifacts in the physically-based mode.")]
[SerializeField, FormerlySerializedAs("highQualityFiltering")]
BoolParameter m_HighQualityFiltering = new BoolParameter(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma kernel KMain

#pragma multi_compile _ ENABLE_ALPHA
#pragma multi_compile _ HIGH_QUALITY

CBUFFER_START(cb0)
float4 _Params;
Expand Down Expand Up @@ -42,13 +43,17 @@ void KMain(uint3 dispatchThreadId : SV_DispatchThreadID)
centerSample.color = GetColorSample(posInputs.positionSS, 0);
centerSample.CoC = GetCoCRadius(posInputs.positionSS);


#ifndef HIGH_QUALITY
int tileClass = GetTileClass(posInputs.positionSS);
if (ResScale != 1.0 && tileClass != FAST_DEFOCUS_TILE)
{
// Early exit: these tiles will be computed at full res in the combine pass
// This might create small artifacts during upscale of the half-res tiles (bilinear fetch at the border picks unblurred values and this might be visible), so it's disabled in high quality mode
_OutputTexture[COORD_TEXTURE2D_X(posInputs.positionSS)] = (CTYPE)centerSample.color;
return;
}
#endif

DoFTile tileData;
LoadTileData(posInputs.positionSS, centerSample, NumRings, tileData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ TEXTURE2D_X(_TileList);

// A set of Defines to fine-tune the algorithm
#define ADAPTIVE_SAMPLING
#define STRATIFY
#define NON_UNIFORM_DENSITY
#define RING_OCCLUSION
#define PER_TILE_BG_FG_CLASSIFICATION
//#define PER_TILE_BG_FG_CLASSIFICATION // Disabled because of artifacts (case 1413534)
#define PHYSICAL_WEIGHTS
#define FORCE_POINT_SAMPLING
//#define USE_BLUE_NOISE
//#define USE_BLUE_NOISE // Increase quality at the cost of performance

#ifndef NON_UNIFORM_DENSITY
#define AlphaScale 1
#else
#define AlphaScale 2
#endif

// ============== RNG Utils ==================== //

Expand Down Expand Up @@ -118,7 +124,8 @@ void LoadTileData(float2 sampleTC, SampleData centerSample, float rings, inout D

// Note: for the far-field, we don't need to search further than than the central CoC.
// If there is a larger CoC that overlaps the central pixel then it will have greater depth
tileData.maxRadius = max(2 * abs(centerSample.CoC), -cocRanges.w) * OneOverResScale;
float limit = min(cocRanges.y, 2 * abs(centerSample.CoC));
tileData.maxRadius = max(limit, -cocRanges.w) * OneOverResScale;

// Detect tiles than need more samples
tileData.numSamples = rings;
Expand Down Expand Up @@ -163,18 +170,31 @@ void AccumulateSample(SampleData sampleData, float weight, inout AccumData accum
#endif
}

void AccumulateCenterSample(SampleData centerSample, inout AccumData accumData)
CTYPE ResolveBackGroundEstimate(SampleData centerSample, float fgAlpha, float4 bgEstimate, float4 bgEstimateAlpha)
{
float centerAlpha = GetSampleWeight(centerSample.CoC);
CTYPE result;
result.xyz = bgEstimate.w > 0 ? bgEstimate.xyz / bgEstimate.w : centerSample.color.xyz;
result.xyz = lerp(centerSample.color.xyz, result.xyz, sqrt(fgAlpha));

#ifdef ENABLE_ALPHA
result.w = bgEstimate.w > 0 ? bgEstimateAlpha / bgEstimate.w : centerSample.color.w;
result.w = lerp(centerSample.color.w, result.w, sqrt(fgAlpha));
#endif

accumData.color.xyz = accumData.color.xyz * (1 - centerAlpha) + centerAlpha * centerSample.color.xyz;
accumData.color.w = accumData.color.w * (1 - centerAlpha) + centerAlpha;
return result;
}

void AccumulateCenterSample(SampleData centerSample, inout AccumData bgAccumData)
{
float centerAlpha = GetSampleWeight(centerSample.CoC);
bgAccumData.color.xyz = bgAccumData.color.xyz * (1 - centerAlpha) + centerAlpha * centerSample.color.xyz;
#ifdef ENABLE_ALPHA
accumData.alpha = accumData.alpha * (1 - centerAlpha) + centerAlpha * centerSample.color.w;
bgAccumData.alpha = bgAccumData.alpha * (1 - centerAlpha) + centerAlpha * centerSample.color.w;
#endif
}

void AccumulateSampleData(SampleData sampleData[2], SampleData centerSample, float sampleRadius, float borderRadius, float layerBorder, const bool isForeground, inout AccumData ringAccum, inout AccumData accumData)

void AccumulateSampleData(SampleData sampleData[2], float sampleRadius, float borderRadius, float layerBorder, float densityBias, const bool isForeground, inout AccumData ringAccum, inout AccumData accumData)
{
UNITY_UNROLL
for (int k = 0; k < 2; k++)
Expand All @@ -185,7 +205,7 @@ void AccumulateSampleData(SampleData sampleData[2], SampleData centerSample, flo

float CoC = abs(sampleData[k].CoC);

float sampleWeight = GetSampleWeight(CoC);
float sampleWeight = densityBias * GetSampleWeight(CoC);
//float visibility = saturate(CoC - sampleRadius);
float visibility = step(0.0, CoC - sampleRadius);

Expand All @@ -198,19 +218,6 @@ void AccumulateSampleData(SampleData sampleData[2], SampleData centerSample, flo
float weight = layerWeight * visibility * sampleWeight;
AccumulateSample(sampleData[k], borderWeight * weight, accumData);
AccumulateSample(sampleData[k], (1.0 - borderWeight) * weight, ringAccum);

#if 0
// Disabled for now due to artifacts
// Mirroring improves the near blur, but since the background reconstruction is not perfect, we limit the radius it is applied
const float mirrorLimit = 2;
const float radius = sampleRadius - CoC;
if (ringData.isForeground && visibility == 0 && radius < mirrorLimit)
{
int pairIndex = k == 0 ? 1 : 0;
float mirrorWeight = 1;
//AccumulateSample(sampleData[pairIndex], mirrorWeight * sampleWeight, ringAccum);
}
#endif
}
}

Expand All @@ -228,7 +235,7 @@ void AccumulateRingData(float numSamples, const bool isNearField, AccumData ring
float ringOcclusion = saturate(accumAvgCoC - ringAvgCoC);
//float ringOpacity = 1.0 - saturate(ringData.coverage * rcp(numSamples));
float normCoC = ringData.CoC * rcp(ringData.color.w);
float ringOpacity = saturate(ringData.color.w * rcp(GetSampleWeight(normCoC)) * rcp(numSamples));
float ringOpacity = saturate(AlphaScale * ringData.color.w * rcp(GetSampleWeight(normCoC)) * rcp(numSamples));

// Near-field is the region where CoC > 0. In this case sorting is reversed.
if (isNearField)
Expand Down Expand Up @@ -282,6 +289,8 @@ void DoFGatherRings(PositionInputs posInputs, DoFTile tileData, SampleData cente
float lod = min(MaxColorMip, log2(2 * PI * tileData.maxRadius * rcp(tileData.numSamples)));

RngStateType rngState = InitRNG(posInputs.positionSS.xy, noiseOffset, 0, tileData.numSamples);
float4 bgEstimate = 0;
float bgEstimateAlpha = 0;

// Gather the DoF samples
for (int ring = tileData.numSamples - 1; ring >= 0; ring--)
Expand All @@ -300,51 +309,56 @@ void DoFGatherRings(PositionInputs posInputs, DoFTile tileData, SampleData cente
float r2 = RandomFloat01(rngState, ring * tileData.numSamples * 2 + 2 * i + 1);
#endif

#ifdef STRATIFY
#ifndef NON_UNIFORM_DENSITY
float densityBias = 1;
float sampleRadius = sqrt((ring + r1) * dR) * tileData.maxRadius;
float borderRadius = sqrt((ring + 1.5) * dR) * tileData.maxRadius;
#else
float sampleRadius = sqrt(r2) * tileData.maxRadius;
float densityBias = ((ring + r1) * dR);
float sampleRadius = ((ring + r1) * dR) * tileData.maxRadius;
float borderRadius = ((ring + 1.5) * dR) * tileData.maxRadius;
#endif
float borderRadius = sqrt((ring + 1.5) * dR) * tileData.maxRadius;

SampleData sampleData[2];
const float offset[2] = { 0, PI };

UNITY_UNROLL
for (int j = 0; j < 2; j++)
{
#ifdef STRATIFY
float2 sampleTC = posInputs.positionSS + sampleRadius * PointInCircle(offset[j] + (i + r2) * dAng);
#else
float2 sampleTC = posInputs.positionSS + sampleRadius * PointInCircle(offset[j] + r2 * PI);
#endif
sampleData[j].color = GetColorSample(sampleTC, lod);
sampleData[j].CoC = GetCoCRadius(sampleTC);
bgEstimate += float4(sampleData[j].color.xyz, 1);
#ifdef ENABLE_ALPHA
bgEstimateAlpha += sampleData[j].color.w;
#endif
}

const float borderFudgingFactor = 9;
float layerBorder = min(0, tileData.layerBorder - borderFudgingFactor * r2);
AccumulateSampleData(sampleData, centerSample, sampleRadius, borderRadius, layerBorder, false, bgRingData, bgAccumData);
AccumulateSampleData(sampleData, centerSample, sampleRadius, borderRadius, layerBorder, true, fgRingData, fgAccumData);
float layerBorder = tileData.layerBorder;
AccumulateSampleData(sampleData, sampleRadius, borderRadius, layerBorder, densityBias, false, bgRingData, bgAccumData);
AccumulateSampleData(sampleData, sampleRadius, borderRadius, layerBorder, densityBias, true, fgRingData, fgAccumData);
}

AccumulateRingData(tileData.numSamples, isBgLayerInNearField, bgRingData, bgAccumData);
AccumulateRingData(tileData.numSamples, true, fgRingData, fgAccumData);
}

ResolveColorAndAlpha(bgAccumData.color, bgAccumData.alpha, centerSample.color);
ResolveColorAndAlpha(fgAccumData.color, fgAccumData.alpha, centerSample.color);

// Accumulate center sample in bg
AccumulateCenterSample(centerSample, bgAccumData);

// Compute the fg alpha. Needs to be normalized based on search radius.
float normCoC = fgAccumData.CoC / fgAccumData.color.w;
float scaleFactor = (normCoC * normCoC) / (tileData.maxRadius * tileData.maxRadius);
float correctSamples = scaleFactor * (tileData.numSamples * tileData.numSamples);
float fgAlpha = saturate(2 * AlphaScale * fgAccumData.color.w * rcp(GetSampleWeight(normCoC)) * rcp(correctSamples));

// Approximate the missing background
CTYPE bgColor = ResolveBackGroundEstimate(centerSample, fgAlpha, bgEstimate, bgEstimateAlpha);

ResolveColorAndAlpha(bgAccumData.color, bgAccumData.alpha, bgColor);
ResolveColorAndAlpha(fgAccumData.color, fgAccumData.alpha, bgColor);

// Note: this extra step is used to hide some artifacts, generally it should not be needed
AccumulateCenterSample(centerSample, bgAccumData);

// Now blend the bg and fg layes
float fgAlpha = saturate(2 * fgAccumData.color.w * rcp(GetSampleWeight(normCoC)) * rcp(correctSamples));
// Now blend the bg and fg layers
color = bgAccumData.color * (1.0 - fgAlpha) + fgAlpha * fgAccumData.color;
alpha = bgAccumData.alpha * (1.0 - fgAlpha) + fgAlpha * fgAccumData.alpha;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2186,6 +2186,11 @@ DepthOfFieldParameters PrepareDoFParameters(HDCamera hdCamera)
parameters.ditheredTextureSet = GetBlueNoiseManager().DitheredTextureSet256SPP();
// Fix the resolution to half. This only affects the out-of-focus regions (and there is no visible benefit at computing those at higher res). Tiles with pixels near the focus plane always run at full res.
parameters.resolution = DepthOfFieldResolution.Half;

if (parameters.highQualityFiltering)
{
parameters.pbDoFGatherCS.EnableKeyword("HIGH_QUALITY");
}
}

if (hdCamera.msaaEnabled)
Expand Down Expand Up @@ -2708,7 +2713,10 @@ static void ReprojectCoCHistory(in DepthOfFieldParameters parameters, CommandBuf
//Note: this reprojection creates some ghosting, we should replace it with something based on the new TAA
ComputeShader cs = parameters.dofCoCReprojectCS;
int kernel = parameters.dofCoCReprojectKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(parameters.resetPostProcessingHistory ? 0f : 0.91f, cocHistoryScale.z, cocHistoryScale.w, 0f));
// This is a fixed empirical value. Was initially 0.91 but was creating a lot of ghosting trails in DoF.
// Looks like we can push it down to 0.86 and still get nice stable results.
float cocHysteresis = 0.86f;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(parameters.resetPostProcessingHistory ? 0f : cocHysteresis, cocHistoryScale.z, cocHistoryScale.w, 0f));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputHistoryCoCTexture, prevCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputCoCTexture, nextCoC);
Expand Down

0 comments on commit 3b27c69

Please sign in to comment.