|
|
@@ -5,16 +5,15 @@
|
|
|
|
|
|
// Does SSGI and GI probe sampling
|
|
|
|
|
|
-#define RECALCULATE_NORMAL false
|
|
|
-#define REJECT_FAR_FROM_HITPOINT true
|
|
|
-#define REJECT_BACKFACING false
|
|
|
+ANKI_SPECIALIZATION_CONSTANT_U32(SAMPLE_COUNT, 6u);
|
|
|
+
|
|
|
#define ENABLE_SSGI true
|
|
|
#define ENABLE_PROBES true
|
|
|
-#define REMOVE_FIREFLIES true
|
|
|
-#define SSGI_PROBE_COMBINE(ssgiColor, probeColor) max((ssgiColor), (probeColor))
|
|
|
+#define REMOVE_FIREFLIES false
|
|
|
+#define REPROJECT_LIGHTBUFFER false
|
|
|
+#define SSGI_PROBE_COMBINE(ssgiColor, probeColor) ((ssgiColor) + (probeColor))
|
|
|
|
|
|
#pragma anki start comp
|
|
|
-#include <AnKi/Shaders/SsRaymarching.glsl>
|
|
|
#include <AnKi/Shaders/Functions.glsl>
|
|
|
#include <AnKi/Shaders/PackFunctions.glsl>
|
|
|
#include <AnKi/Shaders/ImportanceSampling.glsl>
|
|
|
@@ -31,23 +30,26 @@ layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y) in;
|
|
|
#include <AnKi/Shaders/ClusteredShadingCommon.glsl>
|
|
|
|
|
|
layout(set = 0, binding = 4) writeonly uniform image2D u_outImage;
|
|
|
-layout(set = 0, binding = 5) writeonly uniform image2D u_momentsAndHistoryLengthImage;
|
|
|
-
|
|
|
-layout(set = 0, binding = 6) uniform sampler u_trilinearClampSampler;
|
|
|
-layout(set = 0, binding = 7) uniform texture2D u_gbufferRt2;
|
|
|
-layout(set = 0, binding = 8) uniform texture2D u_depthRt;
|
|
|
-layout(set = 0, binding = 9) uniform texture2D u_lightBufferRt;
|
|
|
-layout(set = 0, binding = 10) uniform texture2D u_historyTex;
|
|
|
-layout(set = 0, binding = 11) uniform texture2D u_motionVectorsTex;
|
|
|
-layout(set = 0, binding = 12) uniform texture2D u_motionVectorsRejectionTex;
|
|
|
-layout(set = 0, binding = 13) uniform texture2D u_prevMomentsAndHistoryLengthTex;
|
|
|
-layout(set = 0, binding = 14) uniform texture2D u_ssaoTex;
|
|
|
+
|
|
|
+layout(set = 0, binding = 5) uniform sampler u_linearAnyClampSampler;
|
|
|
+layout(set = 0, binding = 6) uniform texture2D u_gbufferRt2;
|
|
|
+layout(set = 0, binding = 7) uniform texture2D u_depthRt;
|
|
|
+layout(set = 0, binding = 8) uniform texture2D u_lightBufferRt;
|
|
|
+layout(set = 0, binding = 9) uniform texture2D u_historyTex;
|
|
|
+layout(set = 0, binding = 10) uniform texture2D u_motionVectorsTex;
|
|
|
+layout(set = 0, binding = 11) uniform texture2D u_motionVectorsRejectionTex;
|
|
|
|
|
|
layout(push_constant, std430) uniform b_pc
|
|
|
{
|
|
|
IndirectDiffuseUniforms u_unis;
|
|
|
};
|
|
|
|
|
|
+Vec4 cheapProject(Vec4 point)
|
|
|
+{
|
|
|
+ return projectPerspective(point, u_unis.m_projectionMat.x, u_unis.m_projectionMat.y, u_unis.m_projectionMat.z,
|
|
|
+ u_unis.m_projectionMat.w);
|
|
|
+}
|
|
|
+
|
|
|
void main()
|
|
|
{
|
|
|
const UVec2 fixedGlobalInvocationId = min(gl_GlobalInvocationID.xy, u_unis.m_viewportSize);
|
|
|
@@ -56,104 +58,78 @@ void main()
|
|
|
const Vec2 ndc = UV_TO_NDC(uv);
|
|
|
|
|
|
// Get normal
|
|
|
- const Vec3 worldNormal = readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, uv);
|
|
|
+ const Vec3 worldNormal = readNormalFromGBuffer(u_gbufferRt2, u_linearAnyClampSampler, uv);
|
|
|
const Vec3 viewNormal = u_clusteredShading.m_matrices.m_viewRotation * worldNormal;
|
|
|
|
|
|
- // Get depth
|
|
|
- const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, uv, 0.0).r;
|
|
|
+ // Get origin
|
|
|
+ const F32 depth = textureLod(u_depthRt, u_linearAnyClampSampler, uv, 0.0).r;
|
|
|
+ Vec4 v4 = u_clusteredShading.m_matrices.m_invertedViewProjectionJitter * Vec4(ndc, depth, 1.0);
|
|
|
+ const Vec3 worldPos = v4.xyz / v4.w;
|
|
|
+ v4 = u_clusteredShading.m_matrices.m_invertedProjectionJitter * Vec4(ndc, depth, 1.0);
|
|
|
+ const Vec3 viewPos = v4.xyz / v4.w;
|
|
|
|
|
|
- // Compute view pos
|
|
|
- const Vec4 viewPos4 = u_clusteredShading.m_matrices.m_invertedProjectionJitter * Vec4(ndc, depth, 1.0);
|
|
|
- const Vec3 viewPos = viewPos4.xyz / viewPos4.w;
|
|
|
-
|
|
|
- // Get a random point inside the hemisphere. Use hemisphereSampleCos to avoid perpendicular vecs to viewNormal
|
|
|
- const UVec2 random = rand3DPCG16(UVec3(fixedGlobalInvocationId, u_clusteredShading.m_frame)).xy;
|
|
|
- Vec2 randomCircle = hammersleyRandom16(0u, 0xFFFFu, random);
|
|
|
- randomCircle.x *= 0.9; // Reduce the cone angle a bit to avoid self-collisions
|
|
|
- randomCircle.x = pow(randomCircle.x, 4.0); // Get more samples closer to the normal
|
|
|
- const Vec3 randomHemisphere = rotationFromDirection(viewNormal) * hemisphereSampleCos(randomCircle);
|
|
|
-
|
|
|
- // Trace
|
|
|
- Vec3 hitPoint;
|
|
|
- F32 hitAttenuation;
|
|
|
+ // SSGI
|
|
|
+ Vec3 outColor = Vec3(0.0);
|
|
|
+ F32 ssao = 0.0;
|
|
|
if(ENABLE_SSGI)
|
|
|
{
|
|
|
- const U32 lod = 0u;
|
|
|
- const F32 minStepf = 4.0;
|
|
|
- const F32 noise = F32(random.x) * (1.0 / 65536.0);
|
|
|
- const U32 initialStep = U32(mix(minStepf, F32(u_unis.m_stepIncrement), noise));
|
|
|
- raymarchGroundTruth(viewPos, randomHemisphere, uv, depth, u_clusteredShading.m_matrices.m_projectionJitter,
|
|
|
- u_unis.m_maxSteps, u_depthRt, u_trilinearClampSampler, F32(lod), u_unis.m_depthBufferSize,
|
|
|
- u_unis.m_stepIncrement, initialStep, hitPoint, hitAttenuation);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- hitAttenuation = 0.0;
|
|
|
- }
|
|
|
-
|
|
|
- // Reject backfacing
|
|
|
- ANKI_BRANCH if(REJECT_BACKFACING && hitAttenuation > 0.0)
|
|
|
- {
|
|
|
- const Vec3 hitNormal = u_clusteredShading.m_matrices.m_viewRotation
|
|
|
- * readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, hitPoint.xy);
|
|
|
- F32 backFaceAttenuation;
|
|
|
- rejectBackFaces(randomHemisphere, hitNormal, backFaceAttenuation);
|
|
|
-
|
|
|
- hitAttenuation *= backFaceAttenuation;
|
|
|
- }
|
|
|
+ // Find the projected radius
|
|
|
+ const Vec3 sphereLimit = viewPos + Vec3(u_unis.m_radius, 0.0, 0.0);
|
|
|
+ const Vec4 projSphereLimit = cheapProject(Vec4(sphereLimit, 1.0));
|
|
|
+ const Vec2 projSphereLimit2 = projSphereLimit.xy / projSphereLimit.w;
|
|
|
+ const F32 projRadius = length(projSphereLimit2 - ndc);
|
|
|
+
|
|
|
+ // Loop to compute
|
|
|
+ const UVec2 random = rand3DPCG16(UVec3(gl_GlobalInvocationID.xy, u_clusteredShading.m_frame)).xy;
|
|
|
+ for(U32 i = 0u; i < u_unis.m_sampleCount; ++i)
|
|
|
+ {
|
|
|
+ const Vec2 point = UV_TO_NDC(hammersleyRandom16(i, u_unis.m_sampleCount, random));
|
|
|
+ const Vec2 finalDiskPoint = ndc + point * projRadius;
|
|
|
+
|
|
|
+ // Do a cheap unproject in view space
|
|
|
+ const F32 d = textureLod(u_depthRt, u_linearAnyClampSampler, NDC_TO_UV(finalDiskPoint), 0.0).r;
|
|
|
+ const F32 z = u_clusteredShading.m_matrices.m_unprojectionParameters.z
|
|
|
+ / (u_clusteredShading.m_matrices.m_unprojectionParameters.w + d);
|
|
|
+ const Vec2 xy = finalDiskPoint * u_clusteredShading.m_matrices.m_unprojectionParameters.xy * z;
|
|
|
+ const Vec3 s = Vec3(xy, z);
|
|
|
+
|
|
|
+ // Compute factor
|
|
|
+ const Vec3 dir = s - viewPos;
|
|
|
+ const F32 len = length(dir);
|
|
|
+ const Vec3 n = dir / len;
|
|
|
+ const F32 NoL = max(0.0, dot(viewNormal, n));
|
|
|
+ // const F32 distFactor = 1.0 - sin(min(1.0, len / u_unis.m_radius) * PI / 2.0);
|
|
|
+ const F32 distFactor = 1.0 - min(1.0, len / u_unis.m_radius);
|
|
|
+
|
|
|
+ // Compute the UV for sampling the pyramid
|
|
|
+ const Vec2 crntFrameUv = NDC_TO_UV(finalDiskPoint);
|
|
|
+ Vec2 lastFrameUv;
|
|
|
+ if(REPROJECT_LIGHTBUFFER)
|
|
|
+ {
|
|
|
+ lastFrameUv =
|
|
|
+ crntFrameUv + textureLod(u_motionVectorsTex, u_linearAnyClampSampler, crntFrameUv, 0.0).xy;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ lastFrameUv = crntFrameUv;
|
|
|
+ }
|
|
|
|
|
|
- // Reject far from hit point
|
|
|
- ANKI_BRANCH if(REJECT_FAR_FROM_HITPOINT && hitAttenuation > 0.0)
|
|
|
- {
|
|
|
- const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
|
|
|
- Vec4 viewPos4 =
|
|
|
- u_clusteredShading.m_matrices.m_invertedProjectionJitter * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
|
|
|
- const F32 actualZ = viewPos4.z / viewPos4.w;
|
|
|
-
|
|
|
- viewPos4 =
|
|
|
- u_clusteredShading.m_matrices.m_invertedProjectionJitter * Vec4(UV_TO_NDC(hitPoint.xy), hitPoint.z, 1.0);
|
|
|
- const F32 hitZ = viewPos4.z / viewPos4.w;
|
|
|
-
|
|
|
- const F32 rejectionMeters = 1.0;
|
|
|
- const F32 diff = abs(actualZ - hitZ);
|
|
|
- const F32 distAttenuation = (diff < rejectionMeters) ? 1.0 : 0.0;
|
|
|
- hitAttenuation *= distAttenuation;
|
|
|
- }
|
|
|
+ // Append color
|
|
|
+ const F32 w = distFactor * NoL;
|
|
|
+ const Vec3 c = textureLod(u_lightBufferRt, u_linearAnyClampSampler, lastFrameUv, 100.0).xyz;
|
|
|
+ outColor += c * w;
|
|
|
|
|
|
- // Read the light buffer
|
|
|
- Vec3 outColor = Vec3(0.0);
|
|
|
- ANKI_BRANCH if(hitAttenuation > 0.0)
|
|
|
- {
|
|
|
- // Reproject the UV because you are reading the previous frame
|
|
|
- const Vec4 v4 = u_clusteredShading.m_matrices.m_reprojection * Vec4(UV_TO_NDC(hitPoint.xy), hitPoint.z, 1.0);
|
|
|
- hitPoint.xy = NDC_TO_UV(v4.xy / v4.w);
|
|
|
-
|
|
|
- // Read the light buffer
|
|
|
- outColor = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, 2.0).rgb;
|
|
|
- outColor = clamp(outColor, 0.0, FLT_MAX); // Fix the value just in case
|
|
|
- outColor *= hitAttenuation;
|
|
|
-
|
|
|
- // Compute a new normal based on the new hit point
|
|
|
- Vec3 newViewNormal;
|
|
|
- if(RECALCULATE_NORMAL)
|
|
|
- {
|
|
|
- const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
|
|
|
- const Vec4 viewPos4 =
|
|
|
- u_clusteredShading.m_matrices.m_invertedProjectionJitter * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
|
|
|
- const Vec3 hitViewPos = viewPos4.xyz / viewPos4.w;
|
|
|
- newViewNormal = normalize(hitViewPos - viewPos);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- newViewNormal = viewNormal;
|
|
|
+ // Compute SSAO as well
|
|
|
+ ssao += max(dot(viewNormal, dir) + u_unis.m_ssaoBias, EPSILON) / max(len * len, EPSILON);
|
|
|
}
|
|
|
|
|
|
- // Modulate
|
|
|
- const F32 NoL = max(0.0, dot(randomHemisphere, newViewNormal));
|
|
|
- outColor *= NoL;
|
|
|
- outColor *= 2.0 * PI;
|
|
|
+ const F32 scount = 1.0 / u_unis.m_sampleCountf;
|
|
|
+ outColor *= scount * 2.0 * PI;
|
|
|
+ ssao *= scount;
|
|
|
}
|
|
|
|
|
|
+ ssao = min(1.0, 1.0 - ssao * u_unis.m_ssaoStrength);
|
|
|
+
|
|
|
if(ENABLE_PROBES)
|
|
|
{
|
|
|
// Sample probes
|
|
|
@@ -175,7 +151,7 @@ void main()
|
|
|
|
|
|
// Sample
|
|
|
probeColor = sampleGlobalIllumination(worldPos, worldNormal, probe, u_globalIlluminationTextures,
|
|
|
- u_trilinearClampSampler);
|
|
|
+ u_linearAnyClampSampler);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -198,7 +174,7 @@ void main()
|
|
|
|
|
|
// Sample
|
|
|
const Vec3 c = sampleGlobalIllumination(worldPos, worldNormal, probe, u_globalIlluminationTextures,
|
|
|
- u_trilinearClampSampler);
|
|
|
+ u_linearAnyClampSampler);
|
|
|
probeColor += c * blendWeight;
|
|
|
}
|
|
|
|
|
|
@@ -219,53 +195,23 @@ void main()
|
|
|
}
|
|
|
|
|
|
// Apply SSAO
|
|
|
- outColor *= textureLod(u_ssaoTex, u_trilinearClampSampler, uv, 0.0).x;
|
|
|
-
|
|
|
- // Compute history length
|
|
|
- const Vec2 historyUv = uv + textureLod(u_motionVectorsTex, u_trilinearClampSampler, uv, 0.0).xy;
|
|
|
- const F32 historyRejectionFactor = textureLod(u_motionVectorsRejectionTex, u_trilinearClampSampler, uv, 0.0).x;
|
|
|
- const Vec3 prevMomentsAndHistoryLength =
|
|
|
- textureLod(u_prevMomentsAndHistoryLengthTex, u_trilinearClampSampler, historyUv, 0.0).xyz;
|
|
|
- F32 historyLength;
|
|
|
- if(historyRejectionFactor >= 0.5)
|
|
|
- {
|
|
|
- // Rejection factor too high, reset the temporal history
|
|
|
- historyLength = 0.0;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Sample seems stable, increment its temporal history
|
|
|
- historyLength = min(prevMomentsAndHistoryLength.z + 1.0, 128.0);
|
|
|
- }
|
|
|
+ outColor *= ssao;
|
|
|
|
|
|
// Blend color with history
|
|
|
{
|
|
|
- // Compute blend fractor. Use nearest sampler because it's an integer texture
|
|
|
+ const Vec2 historyUv = uv + textureLod(u_motionVectorsTex, u_linearAnyClampSampler, uv, 0.0).xy;
|
|
|
+ const F32 historyRejectionFactor = textureLod(u_motionVectorsRejectionTex, u_linearAnyClampSampler, uv, 0.0).x;
|
|
|
+
|
|
|
const F32 lowestBlendFactor = 0.1;
|
|
|
- const F32 stableFrames = 4.0;
|
|
|
- const F32 lerp = min(1.0, (historyLength + 1.0) / stableFrames);
|
|
|
- const F32 blendFactor = mix(1.0, lowestBlendFactor, lerp);
|
|
|
+ const F32 blendFactor = mix(lowestBlendFactor, 1.0, historyRejectionFactor);
|
|
|
|
|
|
// Blend with history
|
|
|
- const Vec3 history = textureLod(u_historyTex, u_trilinearClampSampler, historyUv, 0.0).rgb;
|
|
|
+ const Vec3 history = textureLod(u_historyTex, u_linearAnyClampSampler, historyUv, 0.0).rgb;
|
|
|
outColor = mix(history, outColor, blendFactor);
|
|
|
}
|
|
|
|
|
|
// Store color
|
|
|
imageStore(u_outImage, IVec2(fixedGlobalInvocationId), Vec4(outColor, 1.0));
|
|
|
-
|
|
|
- // Compute the moments that will give temporal variance
|
|
|
- Vec2 moments;
|
|
|
- moments.x = computeLuminance(outColor);
|
|
|
- moments.y = moments.x * moments.x;
|
|
|
-
|
|
|
- // Blend the moments with history
|
|
|
- F32 momentsBlendFactor = 0.2;
|
|
|
- momentsBlendFactor = mix(momentsBlendFactor, 1.0, historyRejectionFactor);
|
|
|
- moments = mix(prevMomentsAndHistoryLength.xy, moments, momentsBlendFactor);
|
|
|
-
|
|
|
- // Store the moments + history len
|
|
|
- imageStore(u_momentsAndHistoryLengthImage, IVec2(fixedGlobalInvocationId), Vec4(moments, historyLength, 0.0));
|
|
|
}
|
|
|
|
|
|
#pragma anki end
|