|
|
@@ -3,7 +3,7 @@
|
|
|
// Code licensed under the BSD License.
|
|
|
// http://www.anki3d.org/LICENSE
|
|
|
|
|
|
-// Dose SSGI and GI probe sampling
|
|
|
+// Does SSGI and GI probe sampling
|
|
|
|
|
|
#pragma anki start comp
|
|
|
#include <AnKi/Shaders/SsRaymarching.glsl>
|
|
|
@@ -11,42 +11,49 @@
|
|
|
#include <AnKi/Shaders/PackFunctions.glsl>
|
|
|
#include <AnKi/Shaders/ImportanceSampling.glsl>
|
|
|
#include <AnKi/Shaders/TonemappingFunctions.glsl>
|
|
|
-#include <AnKi/Shaders/Include/SsgiTypes.h>
|
|
|
+#include <AnKi/Shaders/Include/IndirectDiffuseTypes.h>
|
|
|
|
|
|
const UVec2 WORKGROUP_SIZE = UVec2(8, 8);
|
|
|
layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y) in;
|
|
|
|
|
|
-layout(set = 0, binding = 0, rgba16f) uniform image2D out_img;
|
|
|
+#define CLUSTERED_SHADING_SET 0
|
|
|
+#define CLUSTERED_SHADING_UNIFORMS_BINDING 0
|
|
|
+#define CLUSTERED_SHADING_GI_BINDING 1
|
|
|
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 3
|
|
|
+#include <AnKi/Shaders/ClusteredShadingCommon.glsl>
|
|
|
|
|
|
-layout(set = 0, binding = 1, row_major, std140) uniform b_unis
|
|
|
+layout(set = 0, binding = 4) writeonly uniform image2D out_img;
|
|
|
+
|
|
|
+layout(set = 0, binding = 5) uniform sampler u_trilinearClampSampler;
|
|
|
+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(push_constant, std430) uniform b_pc
|
|
|
{
|
|
|
- SsgiUniforms u_unis;
|
|
|
+ IndirectDiffuseUniforms u_unis;
|
|
|
};
|
|
|
|
|
|
-layout(set = 0, binding = 2) uniform sampler u_trilinearClampSampler;
|
|
|
-layout(set = 0, binding = 3) uniform texture2D u_gbufferRt2;
|
|
|
-layout(set = 0, binding = 4) uniform texture2D u_depthRt;
|
|
|
-layout(set = 0, binding = 5) uniform texture2D u_lightBufferRt;
|
|
|
-
|
|
|
void main()
|
|
|
{
|
|
|
- const UVec2 fixedGlobalInvocationId = min(gl_GlobalInvocationID.xy, u_unis.m_framebufferSize);
|
|
|
- const Vec2 uv = (Vec2(fixedGlobalInvocationId.xy) + 0.5) / Vec2(u_unis.m_framebufferSize);
|
|
|
+ const UVec2 fixedGlobalInvocationId = min(gl_GlobalInvocationID.xy, u_unis.m_viewportSize);
|
|
|
+ const Vec2 fragCoord = Vec2(fixedGlobalInvocationId.xy) + 0.5;
|
|
|
+ const Vec2 uv = fragCoord / u_unis.m_viewportSizef;
|
|
|
const Vec2 ndc = UV_TO_NDC(uv);
|
|
|
|
|
|
// Get normal
|
|
|
const Vec3 worldNormal = readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, uv);
|
|
|
- const Vec3 viewNormal = u_unis.m_normalMat * worldNormal;
|
|
|
+ const Vec3 viewNormal = u_clusteredShading.m_matrices.m_viewRotation * worldNormal;
|
|
|
|
|
|
// Get depth
|
|
|
const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, uv, 0.0).r;
|
|
|
|
|
|
// Compute view pos
|
|
|
- const Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(ndc, depth, 1.0);
|
|
|
+ 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_unis.m_frameCount)).xy;
|
|
|
+ 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
|
|
|
@@ -58,15 +65,16 @@ void main()
|
|
|
const U32 lod = 0u;
|
|
|
const F32 minStepf = 4.0;
|
|
|
const F32 noise = F32(random.x) * (1.0 / 65536.0);
|
|
|
- raymarchGroundTruth(viewPos, randomHemisphere, uv, depth, u_unis.m_projMat, u_unis.m_maxSteps, u_depthRt,
|
|
|
- u_trilinearClampSampler, F32(lod), u_unis.m_depthBufferSize, u_unis.m_firstStepPixels,
|
|
|
- U32(mix(minStepf, F32(u_unis.m_firstStepPixels), noise)), hitPoint, hitAttenuation);
|
|
|
+ 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);
|
|
|
|
|
|
// Reject backfacing
|
|
|
ANKI_BRANCH if(hitAttenuation > 0.0)
|
|
|
{
|
|
|
- const Vec3 hitNormal =
|
|
|
- u_unis.m_normalMat * readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, hitPoint.xy);
|
|
|
+ const Vec3 hitNormal = u_clusteredShading.m_matrices.m_viewRotation
|
|
|
+ * readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, hitPoint.xy);
|
|
|
F32 backFaceAttenuation;
|
|
|
rejectBackFaces(randomHemisphere, hitNormal, backFaceAttenuation);
|
|
|
|
|
|
@@ -78,7 +86,7 @@ void main()
|
|
|
ANKI_BRANCH if(hitAttenuation > 0.0)
|
|
|
{
|
|
|
// Reproject the UV because you are reading the previous frame
|
|
|
- const Vec4 v4 = u_unis.m_prevViewProjMatMulInvViewProjMat * Vec4(UV_TO_NDC(hitPoint.xy), hitPoint.z, 1.0);
|
|
|
+ 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
|
|
|
@@ -89,7 +97,7 @@ void main()
|
|
|
#if 0
|
|
|
// Compute a new normal based on the new hit point
|
|
|
const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
|
|
|
- const Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
|
|
|
+ const Vec4 viewPos4 = u_clusteredShading.m_matrices.m_invertedProjection * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
|
|
|
const Vec3 hitViewPos = viewPos4.xyz / viewPos4.w;
|
|
|
const Vec3 newViewNormal = normalize(hitViewPos - viewPos);
|
|
|
#else
|
|
|
@@ -103,10 +111,57 @@ void main()
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- outColor = Vec3(0.0, 0.0, 0.0);
|
|
|
+ // Fallback to probes
|
|
|
+
|
|
|
+ // Get the cluster
|
|
|
+ Cluster cluster = getClusterFragCoord(Vec3(fragCoord, depth));
|
|
|
+
|
|
|
+ // Get world position
|
|
|
+ const Vec4 worldPos4 = u_clusteredShading.m_matrices.m_invertedViewProjectionJitter * Vec4(ndc, depth, 1.0);
|
|
|
+ const Vec3 worldPos = worldPos4.xyz / worldPos4.w;
|
|
|
+
|
|
|
+ if(bitCount(cluster.m_giProbesMask) == 1)
|
|
|
+ {
|
|
|
+ // All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
|
|
|
+
|
|
|
+ const GlobalIlluminationProbe probe = u_giProbes[findLSB2(cluster.m_giProbesMask)];
|
|
|
+
|
|
|
+ // Sample
|
|
|
+ outColor = sampleGlobalIllumination(worldPos, worldNormal, probe, u_globalIlluminationTextures,
|
|
|
+ u_trilinearClampSampler);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Zero or more than one probes, do a slow path that blends them together
|
|
|
+
|
|
|
+ F32 totalBlendWeight = EPSILON;
|
|
|
+ outColor = Vec3(0.0);
|
|
|
+
|
|
|
+ // Loop probes
|
|
|
+ ANKI_LOOP while(cluster.m_giProbesMask != 0u)
|
|
|
+ {
|
|
|
+ const U32 idx = U32(findLSB2(cluster.m_giProbesMask));
|
|
|
+ cluster.m_giProbesMask &= ~(1u << idx);
|
|
|
+ const GlobalIlluminationProbe probe = u_giProbes[idx];
|
|
|
+
|
|
|
+ // Compute blend weight
|
|
|
+ const F32 blendWeight =
|
|
|
+ computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, probe.m_fadeDistance);
|
|
|
+ totalBlendWeight += blendWeight;
|
|
|
+
|
|
|
+ // Sample
|
|
|
+ const Vec3 c = sampleGlobalIllumination(worldPos, worldNormal, probe, u_globalIlluminationTextures,
|
|
|
+ u_trilinearClampSampler);
|
|
|
+ outColor += c * blendWeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Normalize
|
|
|
+ outColor /= totalBlendWeight;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Remove fireflies
|
|
|
+ if(false)
|
|
|
{
|
|
|
const F32 lum = computeLuminance(outColor) + 0.001;
|
|
|
const F32 averageLum = (subgroupAdd(lum) / F32(gl_SubgroupSize)) * 2.0;
|