Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 39 additions & 33 deletions features/Screen Space GI/Shaders/ScreenSpaceGI/gi.cs.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ Texture2D<float4> srcNormalRoughness : register(t1);
Texture2D<float3> srcRadiance : register(t2); // maybe half-res
Texture2D<unorm float2> srcNoise : register(t3);
Texture2D<unorm float> srcAccumFrames : register(t4); // maybe half-res
Texture2D<float> srcPrevAo : register(t5); // maybe half-res
Texture2D<float4> srcPrevY : register(t6); // maybe half-res
Texture2D<float2> srcPrevCoCg : register(t7); // maybe half-res
Texture2D<float4> srcPrevGISpecular : register(t8); // maybe half-res
Texture2D<float2> srcNormal : register(t9);
Texture2D<float4> srcPrevY : register(t5); // maybe half-res
Texture2D<float2> srcPrevCoCg : register(t6); // maybe half-res
Texture2D<float4> srcPrevGISpecular : register(t7); // maybe half-res
Texture2D<float2> srcNormal : register(t8);

RWTexture2D<unorm float> outAo : register(u0);
RWTexture2D<float4> outY : register(u1);
Expand Down Expand Up @@ -145,13 +144,18 @@ void CalculateGI(
// convert to px units for later use
float2 omega = float2(directionVec.x, -directionVec.y) * screenspaceRadius;

// Per-slice constant for per-step mip selection: log2(length(s * omega)) decomposes
// to log2(s) + logLenOmega for s >= 0. 0.5 * log2(dot) folds length() into a single log2.
const float logLenOmega = 0.5 * log2(max(dot(omega, omega), EPSILON_LENGTH_SQ));

const float3 orthoDirectionVec = directionVec - (dot(directionVec, viewVec) * viewVec);
const float3 axisVec = normalize(cross(orthoDirectionVec, viewVec));

float3 projectedNormalVec = viewspaceNormal - axisVec * dot(viewspaceNormal, axisVec);
float projectedNormalVecLength = length(projectedNormalVec);
// 1/length(v) == rsqrt(dot(v,v)). max() guards against a zero-length projection.
float rcpProjectedNormalVecLength = rsqrt(max(dot(projectedNormalVec, projectedNormalVec), EPSILON_LENGTH_SQ));
float signNorm = sign(dot(orthoDirectionVec, projectedNormalVec));
float cosNorm = saturate(dot(projectedNormalVec, viewVec) / projectedNormalVecLength);
float cosNorm = saturate(dot(projectedNormalVec, viewVec) * rcpProjectedNormalVecLength);

float n = signNorm * FastMath::ACos(cosNorm);

Expand Down Expand Up @@ -189,8 +193,9 @@ void CalculateGI(
float2 sampleScreenPos = Stereo::ConvertFromStereoUV(sampleUV, sampleEyeIndex);
[branch] if (any(sampleScreenPos > 1.0) || any(sampleScreenPos < 0.0)) continue;

float sampleOffsetLength = length(sampleOffset);
float mipLevel = clamp(log2(sampleOffsetLength) - 3.3, 0, 5);
// Mip level grows with pixel-space distance from the centre.
// logLenOmega is the per-slice log2 of |omega|. s > 0 since s += minS > 0.
float mipLevel = clamp(log2(s) + logLenOmega - 3.3, 0, 5);
float mipLevelRadiance = mipLevel;
#if defined(HALF_RES)
mipLevel = max(mipLevel, 1);
Expand Down Expand Up @@ -218,8 +223,8 @@ void CalculateGI(
float3 sampleDelta = samplePos - pixCenterPos;
float3 sampleHorizonVec = normalize(sampleDelta);

float3 sampleBackPos = samplePos - viewVec * Thickness;
float3 sampleBackHorizonVec = normalize(sampleBackPos - pixCenterPos);
// Back-side horizon vector for the surface-thickness offset.
float3 sampleBackHorizonVec = normalize(sampleDelta - viewVec * Thickness);

float angleFront = FastMath::ACos(dot(sampleHorizonVec, viewVec)); // either clamp or use float version for whatever reason
float angleBack = FastMath::ACos(dot(sampleBackHorizonVec, viewVec));
Expand All @@ -232,8 +237,8 @@ void CalculateGI(
uint maskedBits = s < AORadius ? ((1 << bitsRange.y) - 1) << bitsRange.x : 0;

#ifdef GI
float3 sampleBackPosGI = samplePos - viewVec * 300;
float3 sampleBackHorizonVecGI = normalize(sampleBackPosGI - pixCenterPos);
// Back-side horizon vector for GI-radius sampling depth (~300 units).
float3 sampleBackHorizonVecGI = normalize(sampleDelta - viewVec * 300);
float angleBackGI = FastMath::ACos(dot(sampleBackHorizonVecGI, viewVec));
float2 angleRangeGI = -sideSign * (sideSign == -1 ? float2(angleFront, angleBackGI) : float2(angleBackGI, angleFront));

Expand Down Expand Up @@ -369,29 +374,30 @@ void CalculateGI(
#ifdef TEMPORAL_DENOISER
float lerpFactor = rcp(srcAccumFrames[pxCoord] * 255);

// Clamp history to the local color neighborhood to prevent ghosting
// and reduce the magnitude of pops when disocclusion finally fires.
// Standard technique from SVGF (Schied et al. 2017).
float4 prevY = srcPrevY[pxCoord];
float2 prevCoCg = srcPrevCoCg[pxCoord];

// Clamp history to the local color neighbourhood to prevent ghosting
// and reduce pops when disocclusion fires (SVGF, Schied 2017).
// 5-tap cross pattern. Skipped on saturated history: by that point
// the temporal blend has self-stabilised via prior frames' clamps,
// so the marginal bounding effect doesn't justify the bandwidth.
[branch] if (lerpFactor >= 0.15)
{
float4 nMinY = currY, nMaxY = currY;
float2 nMinCoCg = currCoCg, nMaxCoCg = currCoCg;
[unroll] for (int dy = -1; dy <= 1; dy++)
{
[unroll] for (int dx = -1; dx <= 1; dx++)
{
if (dx == 0 && dy == 0)
continue;
int2 np = pxCoord + int2(dx, dy);
float4 nY = srcPrevY[np];
float2 nCC = srcPrevCoCg[np];
nMinY = min(nMinY, nY);
nMaxY = max(nMaxY, nY);
nMinCoCg = min(nMinCoCg, nCC);
nMaxCoCg = max(nMaxCoCg, nCC);
}
}
float4 yL = srcPrevY[pxCoord + int2(-1, 0)];
float4 yR = srcPrevY[pxCoord + int2(1, 0)];
float4 yU = srcPrevY[pxCoord + int2(0, -1)];
float4 yD = srcPrevY[pxCoord + int2(0, 1)];
float2 cL = srcPrevCoCg[pxCoord + int2(-1, 0)];
float2 cR = srcPrevCoCg[pxCoord + int2(1, 0)];
float2 cU = srcPrevCoCg[pxCoord + int2(0, -1)];
float2 cD = srcPrevCoCg[pxCoord + int2(0, 1)];

float4 nMinY = min(min(min(yL, yR), min(yU, yD)), currY);
float4 nMaxY = max(max(max(yL, yR), max(yU, yD)), currY);
float2 nMinCoCg = min(min(min(cL, cR), min(cU, cD)), currCoCg);
float2 nMaxCoCg = max(max(max(cL, cR), max(cU, cD)), currCoCg);

prevY = clamp(prevY, nMinY, nMaxY);
prevCoCg = clamp(prevCoCg, nMinCoCg, nMaxCoCg);
}
Expand Down
1 change: 1 addition & 0 deletions package/Shaders/Common/Math.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define EPSILON_DIVISION 1e-6f // For division to avoid division by zero
#define EPSILON_GLINTS 1e-8f // For glints calculations
#define EPSILON_WEIGHT_SUM 1e-10f // For weight normalization
#define EPSILON_LENGTH_SQ 1e-20f // Minimum dot(v,v) before rsqrt to avoid inf on degenerate vectors

#define DEPTH_SKY_SENTINEL 999999.0f // Linearized depth sentinel for sky/unmapped pixels (beyond any real geometry)

Expand Down
9 changes: 4 additions & 5 deletions src/Features/ScreenSpaceGI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -864,11 +864,10 @@ void ScreenSpaceGI::DrawSSGI()
srvs.at(2) = texRadiance->srv.get();
srvs.at(3) = texNoise->srv.get();
srvs.at(4) = texAccumFrames[lastFrameAccumTexIdx]->srv.get();
srvs.at(5) = texAo[inputAoTexIdx]->srv.get();
srvs.at(6) = texIlY[inputGITexIdx]->srv.get();
srvs.at(7) = texIlCoCg[inputGITexIdx]->srv.get();
srvs.at(8) = texGiSpecular[inputAoTexIdx]->srv.get();
srvs.at(9) = texNormal->srv.get();
srvs.at(5) = texIlY[inputGITexIdx]->srv.get();
srvs.at(6) = texIlCoCg[inputGITexIdx]->srv.get();
srvs.at(7) = texGiSpecular[inputAoTexIdx]->srv.get();
srvs.at(8) = texNormal->srv.get();

uavs.at(0) = texAo[!inputAoTexIdx]->uav.get();
uavs.at(1) = texIlY[!inputGITexIdx]->uav.get();
Expand Down
Loading