Sfoglia il codice sorgente

Compute the SSR in 1/4 resolution

Panagiotis Christopoulos Charitos 7 anni fa
parent
commit
c482e509bb

+ 1 - 0
sandbox/Main.cpp

@@ -51,6 +51,7 @@ Error MyApp::init(int argc, char* argv[])
 	{
 		m_profile = true;
 		setTimerTick(0.0);
+		CoreTracerSingleton::get().m_enabled = true;
 	}
 
 // Input

+ 1 - 6
shaders/LightShading.glslp

@@ -7,7 +7,6 @@
 #pragma anki input const U32 CLUSTER_COUNT_Y
 #pragma anki input const U32 CLUSTER_COUNT_Z
 #pragma anki input const U32 CLUSTER_COUNT
-#pragma anki input const U32 IR_MIPMAP_COUNT
 
 #pragma anki start vert
 #include <shaders/Common.glsl>
@@ -38,7 +37,7 @@ void main()
 #define LIGHT_SET 0
 #define LIGHT_SS_BINDING 0
 #define LIGHT_UBO_BINDING 0
-#define LIGHT_TEX_BINDING 5
+#define LIGHT_TEX_BINDING 4
 #define LIGHT_LIGHTS
 #define LIGHT_COMMON_UNIS
 #include <shaders/ClusterLightCommon.glsl>
@@ -47,7 +46,6 @@ layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msRt0;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_msRt1;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_msRt2;
 layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_msDepthRt;
-layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_indirectRt;
 
 layout(location = 0) in Vec2 in_uv;
 layout(location = 1) in Vec2 in_clusterIJ;
@@ -92,9 +90,6 @@ void main()
 	// Ambient and emissive color
 	out_color = gbuffer.m_diffuse * gbuffer.m_emission;
 
-	// Indirect
-	out_color += textureLod(u_indirectRt, in_uv, 0.0).rgb;
-
 	// Skip decals
 	U32 count = u_lightIndices[idxOffset];
 	idxOffset += count + 1u;

+ 160 - 0
shaders/LightShadingReflectionsAndIndirect.glslp

@@ -0,0 +1,160 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// Apply reflections and indirect lighting
+
+#pragma anki input const U32 CLUSTER_COUNT_X
+#pragma anki input const U32 CLUSTER_COUNT_Y
+#pragma anki input const U32 CLUSTER_COUNT_Z
+#pragma anki input const U32 IR_MIPMAP_COUNT
+
+#pragma anki start vert
+#include <shaders/Common.glsl>
+
+layout(location = 0) out Vec2 out_uv;
+layout(location = 1) out Vec2 out_clusterIJ;
+
+out gl_PerVertex
+{
+	Vec4 gl_Position;
+};
+
+void main()
+{
+	out_uv = Vec2(gl_VertexID & 1, gl_VertexID >> 1) * 2.0;
+	Vec2 pos = out_uv * 2.0 - 1.0;
+	gl_Position = Vec4(pos, 0.0, 1.0);
+
+	out_clusterIJ = Vec2(CLUSTER_COUNT_X, CLUSTER_COUNT_Y) * out_uv;
+}
+#pragma anki end
+
+#pragma anki start frag
+#include <shaders/Functions.glsl>
+#include <shaders/Pack.glsl>
+#include <shaders/glsl_cpp_common/Clusterer.h>
+
+#define LIGHT_SET 0
+#define LIGHT_SS_BINDING 0
+#define LIGHT_UBO_BINDING 0
+#define LIGHT_TEX_BINDING 5
+#define LIGHT_INDIRECT
+#define LIGHT_COMMON_UNIS
+#include <shaders/ClusterLightCommon.glsl>
+
+layout(location = 0) in Vec2 in_uv;
+layout(location = 1) in Vec2 in_clusterIJ;
+layout(location = 0) out Vec3 out_color;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_gbufferRt0;
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_gbufferRt1;
+layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_gbufferRt2;
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_depthRt;
+layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_ssrRt;
+
+// Note: All calculations in world space
+void readReflectionsAndIrradianceFromProbes(U32 idxOffset,
+	Vec3 worldPos,
+	Vec3 normal,
+	Vec3 viewDir,
+	F32 roughness,
+	out Vec3 specIndirect,
+	out Vec3 diffIndirect)
+{
+	specIndirect = Vec3(0.0);
+	diffIndirect = Vec3(0.0);
+
+	Vec3 reflDir = reflect(viewDir, normal);
+
+	F32 reflLod = F32(IR_MIPMAP_COUNT - 1u) * roughness;
+
+	F32 totalBlendWeight = EPSILON;
+
+	// Check proxy
+	U32 count = u_lightIndices[idxOffset++];
+	ANKI_LOOP while(count-- != 0)
+	{
+		ReflectionProbe probe = u_reflectionProbes[u_lightIndices[idxOffset++]];
+		Vec3 aabbMin = probe.m_aabbMinPad1.xyz;
+		Vec3 aabbMax = probe.m_aabbMaxPad1.xyz;
+		Vec3 probeOrigin = probe.m_positionCubemapIndex.xyz;
+		F32 cubemapIndex = probe.m_positionCubemapIndex.w;
+
+		// Compute blend weight
+		F32 blendWeight = computeProbeBlendWeight(worldPos, aabbMin, aabbMax, 0.2);
+		totalBlendWeight += blendWeight;
+
+		// Sample reflections
+		Vec3 cubeUv = intersectProbe(worldPos, reflDir, aabbMin, aabbMax, probeOrigin);
+		Vec3 c = textureLod(u_reflectionsTex, Vec4(cubeUv, cubemapIndex), reflLod).rgb;
+		specIndirect += c * blendWeight;
+
+		// Sample irradiance
+		cubeUv = intersectProbe(worldPos, normal, aabbMin, aabbMax, probeOrigin);
+		c = textureLod(u_irradianceTex, Vec4(cubeUv, cubemapIndex), 0.0).rgb;
+		diffIndirect += c * blendWeight;
+	}
+
+	// Normalize the colors
+	specIndirect /= totalBlendWeight;
+	diffIndirect /= totalBlendWeight;
+}
+
+void main()
+{
+	Vec2 uv = in_uv;
+
+	// Compute world position
+	F32 depth = textureLod(u_depthRt, uv, 0.0).r;
+	Vec4 worldPos4 = u_invViewProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
+	Vec3 worldPos = worldPos4.xyz / worldPos4.w;
+	Vec3 viewDir = normalize(u_cameraPos - worldPos);
+
+	// Get first light index
+	U32 idxOffset;
+	{
+		U32 k = computeClusterK(u_clustererMagic, worldPos);
+		U32 clusterIdx =
+			k * (CLUSTER_COUNT_X * CLUSTER_COUNT_Y) + U32(in_clusterIJ.y) * CLUSTER_COUNT_X + U32(in_clusterIJ.x);
+
+		idxOffset = u_clusters[clusterIdx];
+	}
+
+	// Skip decals
+	U32 count = u_lightIndices[idxOffset++];
+	idxOffset += count;
+
+	// Skip point lights
+	count = u_lightIndices[idxOffset++];
+	idxOffset += count;
+
+	// Skip spot lights
+	count = u_lightIndices[idxOffset++];
+	idxOffset += count;
+
+	// Read gbuffer
+	GbufferInfo gbuffer;
+	readGBuffer(u_gbufferRt0, u_gbufferRt1, u_gbufferRt2, uv, 0.0, gbuffer);
+
+	// Do the probe read
+	Vec3 envColor;
+	Vec3 indirectColor;
+	readReflectionsAndIrradianceFromProbes(
+		idxOffset, worldPos, gbuffer.m_normal, viewDir, gbuffer.m_roughness, envColor, indirectColor);
+
+	// Read the SSL result
+	Vec4 ssr = textureLod(u_ssrRt, uv, 0.0);
+
+	// Combine the SSR and probe reflections and write the result
+	Vec3 finalRefl = envColor * (1.0 - ssr.a) + ssr.rgb;
+
+	// Compute env BRDF
+	F32 NoV = max(EPSILON, dot(gbuffer.m_normal, viewDir));
+	Vec3 env = envBRDF(gbuffer.m_specular, gbuffer.m_roughness, u_integrationLut, NoV);
+
+	// Compute the final color
+	out_color = indirectColor * gbuffer.m_diffuse + finalRefl * env;
+}
+#pragma anki end

+ 13 - 6
shaders/Pack.glsl

@@ -126,8 +126,8 @@ Vec2 unpackUnorm1ToUnorm2(in F32 c)
 #endif
 }
 
-// Max emission. Keep as low as possible
-const F32 MAX_EMISSION = 20.0;
+const F32 MAX_EMISSION = 20.0; // Max emission. Keep as low as possible
+const F32 MIN_ROUGHNESS = 0.05;
 
 // G-Buffer structure
 struct GbufferInfo
@@ -142,7 +142,7 @@ struct GbufferInfo
 };
 
 // Populate the G buffer
-void writeGBuffer(in GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2)
+void writeGBuffer(GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2)
 {
 	rt0 = Vec4(g.m_diffuse, g.m_subsurface);
 	rt1 = Vec4(g.m_roughness, g.m_metallic, g.m_specular.x, 0.0);
@@ -152,9 +152,17 @@ void writeGBuffer(in GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2)
 }
 
 // Read from G-buffer
-void readNormalFromGBuffer(in sampler2D rt2, in Vec2 uv, out Vec3 normal)
+Vec3 readNormalFromGBuffer(sampler2D rt2, Vec2 uv)
 {
-	normal = signedOctDecode(texture(rt2, uv).rga);
+	return signedOctDecode(texture(rt2, uv).rga);
+}
+
+// Read the roughness from G-buffer
+F32 readRoughnessFromGBuffer(sampler2D rt1, Vec2 uv)
+{
+	F32 r = textureLod(rt1, uv, 0.0).r;
+	r = r * (1.0 - MIN_ROUGHNESS) + MIN_ROUGHNESS;
+	return r;
 }
 
 // Read from the G buffer
@@ -174,7 +182,6 @@ void readGBuffer(sampler2D rt0, sampler2D rt1, sampler2D rt2, Vec2 uv, F32 lod,
 	g.m_emission = comp.z * MAX_EMISSION;
 
 	// Fix roughness
-	const F32 MIN_ROUGHNESS = 0.05;
 	g.m_roughness = g.m_roughness * (1.0 - MIN_ROUGHNESS) + MIN_ROUGHNESS;
 
 	// Compute reflectance

+ 0 - 320
shaders/Reflections.glslp

@@ -1,320 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-// if VARIANT==0 then the checkerboard pattern is (render on 'v'):
-// -----
-// |v| |
-// | |v|
-// -----
-
-#pragma anki mutator VARIANT 0 1
-
-#pragma anki input const UVec2 FB_SIZE
-#pragma anki input const UVec2 WORKGROUP_SIZE
-#pragma anki input const U32 MAX_STEPS
-#pragma anki input const U32 LIGHT_BUFFER_MIP_COUNT
-#pragma anki input const U32 HIZ_MIP_COUNT
-#pragma anki input const U32 CLUSTER_COUNT_X
-#pragma anki input const U32 CLUSTER_COUNT_Y
-#pragma anki input const U32 CLUSTER_COUNT_Z
-#pragma anki input const U32 IR_MIPMAP_COUNT
-
-#pragma anki start comp
-#include <shaders/Functions.glsl>
-#include <shaders/Pack.glsl>
-#include <shaders/glsl_cpp_common/Clusterer.h>
-
-#define LIGHT_SET 0
-#define LIGHT_SS_BINDING 0
-#define LIGHT_UBO_BINDING 0
-#define LIGHT_TEX_BINDING 6
-#define LIGHT_INDIRECT
-#define LIGHT_COMMON_UNIS
-#include <shaders/ClusterLightCommon.glsl>
-
-const IVec2 HIZ_SIZE = IVec2(FB_SIZE) >> 1;
-
-layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
-
-layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_gbufferRt0;
-layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_gbufferRt1;
-layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_gbufferRt2;
-layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_depthRt;
-layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_hizRt;
-layout(ANKI_TEX_BINDING(0, 5)) uniform sampler2D u_lightBufferRt;
-
-layout(ANKI_IMAGE_BINDING(0, 0)) writeonly uniform image2D out_reflAndIndirect;
-
-// Temp buffer to hold the indirect color
-shared Vec3 s_pixels[WORKGROUP_SIZE.y][WORKGROUP_SIZE.x];
-
-#define u_normalMat Mat3(u_viewMat)
-
-Vec4 returnSslrColor(Vec3 raySample, F32 factor, F32 roughness)
-{
-	// Re-project previous frame
-	Vec4 v4 = u_prevViewProjMatMulInvViewProjMat * Vec4(UV_TO_NDC(raySample.xy), raySample.z, 1.0);
-	raySample.xy = NDC_TO_UV(v4.xy / v4.w);
-	raySample.xy = saturate(raySample.xy);
-
-	Vec2 ndc = abs(UV_TO_NDC(raySample.xy));
-	F32 contribution = max(ndc.x, ndc.y);
-	contribution = 1.0 - contribution * contribution;
-	contribution *= factor;
-
-	F32 lod = F32(LIGHT_BUFFER_MIP_COUNT - 1u) * roughness;
-	Vec3 color = textureLod(u_lightBufferRt, raySample.xy, lod).rgb;
-	return Vec4(color, contribution);
-}
-
-// Note: All calculations in view space
-Vec4 doSslr(Vec3 r, Vec3 n, Vec3 viewPos, Vec2 uv, F32 depth, F32 roughness)
-{
-	Vec3 p0 = viewPos;
-
-	// Check for view facing reflections [sakibsaikia]
-	Vec3 viewDir = normalize(viewPos);
-	F32 cameraFacingReflectionAttenuation = 1.0 - smoothstep(0.25, 0.5, dot(-viewDir, r));
-	if(cameraFacingReflectionAttenuation <= 0.0)
-	{
-		return Vec4(0.0);
-	}
-
-	// Compute an end point p1. This point is supposed to fall in front of the near plane. Add a small padding to near
-	// to avoid having p1 touching the near plane.
-	Vec3 p1 = p0 + r * (-p0.z - (u_near + 0.1));
-
-	// Start point
-	Vec3 start = Vec3(uv, depth);
-
-	// Project end point
-	Vec4 end4 = u_projMat * Vec4(p1, 1.0);
-	Vec3 end = end4.xyz / end4.w;
-	end.xy = NDC_TO_UV(end.xy);
-
-	// Compute the ray and step size
-	Vec3 ray = end - start;
-	Vec2 texelDims = abs(ray.xy) * Vec2(HIZ_SIZE);
-	F32 stepSize = length(ray.xy) / max(texelDims.x, texelDims.y);
-	ray = normalize(ray);
-
-	// Compute step
-	const U32 BIG_STEP_SKIP = 32u;
-	U32 stepSkip = BIG_STEP_SKIP;
-
-	U32 l = gl_GlobalInvocationID.x & 1u;
-	U32 j = gl_GlobalInvocationID.y & 1u;
-	const U32 STEPS_ARR[4] = U32[](6u, 25u, 13u, 18u);
-	U32 step = STEPS_ARR[l * 2u + j];
-
-	// Iterate
-	Bool found = false;
-	Vec3 raySample;
-	ANKI_LOOP for(U32 iterations = 0u; iterations < MAX_STEPS; ++iterations)
-	{
-		raySample = start + ray * (F32(step) * stepSize);
-
-		// Check if it's out of the view
-		if(raySample.x <= 0.0 || raySample.y <= 0.0 || raySample.x >= 1.0 || raySample.y >= 1.0)
-		{
-			break;
-		}
-
-		F32 depth = textureLod(u_hizRt, raySample.xy, 0.0).r;
-
-		Bool hit = raySample.z - depth >= 0.0;
-		if(!hit)
-		{
-			step += stepSkip;
-		}
-		else if(stepSkip > 1)
-		{
-			step -= BIG_STEP_SKIP - 1u;
-			stepSkip = 1u;
-		}
-		else
-		{
-			found = true;
-			break;
-		}
-	}
-
-	// return Vec4(heatmap(F32(iterations) / F32(MAX_STEPS)), 1.0);
-
-	if(found)
-	{
-		return returnSslrColor(raySample, cameraFacingReflectionAttenuation, roughness);
-	}
-	else
-	{
-		return Vec4(0.0);
-	}
-}
-
-// Note: All calculations in world space
-void readReflectionsAndIrradianceFromProbes(
-	U32 idxOffset, Vec3 worldPos, Vec3 normal, F32 roughness, out Vec3 specIndirect, out Vec3 diffIndirect)
-{
-	specIndirect = Vec3(0.0);
-	diffIndirect = Vec3(0.0);
-
-	Vec3 viewDir = normalize(worldPos - u_cameraPos);
-	Vec3 reflDir = reflect(viewDir, normal);
-
-	F32 reflLod = F32(IR_MIPMAP_COUNT - 1u) * roughness;
-
-	F32 totalBlendWeight = EPSILON;
-
-	// Check proxy
-	U32 count = u_lightIndices[idxOffset++];
-	ANKI_LOOP while(count-- != 0)
-	{
-		ReflectionProbe probe = u_reflectionProbes[u_lightIndices[idxOffset++]];
-		Vec3 aabbMin = probe.m_aabbMinPad1.xyz;
-		Vec3 aabbMax = probe.m_aabbMaxPad1.xyz;
-		Vec3 probeOrigin = probe.m_positionCubemapIndex.xyz;
-		F32 cubemapIndex = probe.m_positionCubemapIndex.w;
-
-		// Compute blend weight
-		F32 blendWeight = computeProbeBlendWeight(worldPos, aabbMin, aabbMax, 0.2);
-		totalBlendWeight += blendWeight;
-
-		// Sample reflections
-		Vec3 cubeUv = intersectProbe(worldPos, reflDir, aabbMin, aabbMax, probeOrigin);
-		Vec3 c = textureLod(u_reflectionsTex, Vec4(cubeUv, cubemapIndex), reflLod).rgb;
-		specIndirect += c * blendWeight;
-
-		// Sample irradiance
-		cubeUv = intersectProbe(worldPos, normal, aabbMin, aabbMax, probeOrigin);
-		c = textureLod(u_irradianceTex, Vec4(cubeUv, cubemapIndex), 0.0).rgb;
-		diffIndirect += c * blendWeight;
-	}
-
-	// Normalize the colors
-	specIndirect /= totalBlendWeight;
-	diffIndirect /= totalBlendWeight;
-}
-
-void main()
-{
-	// Compute a global invocation ID that takes the checkerboard pattern into account
-	IVec2 fixedInvocationId = IVec2(gl_GlobalInvocationID.xy);
-	fixedInvocationId.x *= 2;
-#if VARIANT == 0
-	fixedInvocationId.x += ((fixedInvocationId.y + 1) & 1);
-#else
-	fixedInvocationId.x += ((fixedInvocationId.y + 0) & 1);
-#endif
-
-	if(fixedInvocationId.x >= I32(FB_SIZE.x) || fixedInvocationId.y >= I32(FB_SIZE.y))
-	{
-		// Skip threads outside the writable image
-		return;
-	}
-
-	Vec2 uv = (Vec2(fixedInvocationId) + 0.5) / Vec2(FB_SIZE);
-	Vec2 ndc = UV_TO_NDC(uv);
-
-	// Read gbuffer
-	GbufferInfo gbuffer;
-	readGBuffer(u_gbufferRt0, u_gbufferRt1, u_gbufferRt2, uv, 0.0, gbuffer);
-
-	// Get depth
-	F32 depth = textureLod(u_depthRt, uv, 0.0).r;
-
-	// Get world position
-	Vec4 worldPos4 = u_invViewProjMat * Vec4(ndc, depth, 1.0);
-	Vec3 worldPos = worldPos4.xyz / worldPos4.w;
-
-	// Compute env BRDF
-	Vec3 env;
-	{
-		Vec3 viewDir = normalize(u_cameraPos - worldPos);
-		F32 NoV = max(EPSILON, dot(gbuffer.m_normal, viewDir));
-		env = envBRDF(gbuffer.m_specular, gbuffer.m_roughness, u_integrationLut, NoV);
-	}
-
-	// Try SSR
-	F32 sslrFactor = 0.0;
-	Vec3 sslrCol = Vec3(0.0);
-	if(env.g > 0.05)
-	{
-		// Get view pos
-		Vec4 viewPos4 = u_invProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
-		Vec3 viewPos = viewPos4.xyz / viewPos4.w;
-
-		// Do SSLR
-		Vec3 viewDir = normalize(viewPos);
-		Vec3 viewNormal = u_normalMat * gbuffer.m_normal;
-		Vec3 reflVec = reflect(viewDir, viewNormal);
-
-		Vec4 sslr = doSslr(reflVec, viewNormal, viewPos, uv, depth, gbuffer.m_roughness);
-		sslrFactor = sslr.w;
-		sslrCol = sslr.xyz;
-		sslrCol = clamp(sslrCol, 0.0, FLT_MAX); // Fix the value just in case
-	}
-
-	// Read probes
-	Vec3 probeCol = Vec3(0.0);
-	Vec3 indirectCol = Vec3(0.0);
-	{
-		// Get first light index
-		U32 clusterIdx = computeClusterIndex(u_clustererMagic, uv, worldPos, CLUSTER_COUNT_X, CLUSTER_COUNT_Y);
-		U32 idxOffset = u_clusters[clusterIdx];
-
-		// Skip decals
-		U32 count = u_lightIndices[idxOffset++];
-		idxOffset += count;
-
-		// Skip point lights
-		count = u_lightIndices[idxOffset++];
-		idxOffset += count;
-
-		// Skip spot lights
-		count = u_lightIndices[idxOffset++];
-		idxOffset += count;
-
-		// Do the probe read
-		readReflectionsAndIrradianceFromProbes(
-			idxOffset, worldPos, gbuffer.m_normal, gbuffer.m_roughness, probeCol, indirectCol);
-	}
-
-	// Combine the SSR and probe reflections and write the result
-	Vec3 finalRefl = mix(probeCol, sslrCol, sslrFactor);
-
-	// Compute the final color
-	Vec3 outColor = indirectCol * gbuffer.m_diffuse + finalRefl * env;
-
-	// Store the color for the resolve
-	s_pixels[gl_LocalInvocationID.y][gl_LocalInvocationID.x] = outColor;
-
-	// Wait for all the threads to store their stuff
-	memoryBarrierShared();
-	barrier();
-
-	// Compute the missing pixel by resolving with the right or left neighbour
-	IVec2 readPixel, storePixel;
-	readPixel.y = I32(gl_LocalInvocationID.y);
-	storePixel.y = fixedInvocationId.y;
-
-#if VARIANT == 0
-	Bool pickRightNeighbour = (fixedInvocationId.y & 1) == 1;
-#else
-	Bool pickRightNeighbour = (fixedInvocationId.y & 1) == 0;
-#endif
-	I32 xOffset = (pickRightNeighbour) ? 1 : -1;
-
-	readPixel.x = I32(gl_LocalInvocationID.x) + xOffset;
-	readPixel.x = clamp(readPixel.x, 0, I32(WORKGROUP_SIZE.x - 1));
-
-	storePixel.x = fixedInvocationId.x + xOffset;
-
-	Vec3 missingColor = (outColor + s_pixels[readPixel.y][readPixel.x]) * 0.5; // average
-
-	// Store both the pixels
-	imageStore(out_reflAndIndirect, fixedInvocationId, Vec4(outColor, 0.0));
-	imageStore(out_reflAndIndirect, storePixel, Vec4(missingColor, 0.0));
-}
-#pragma anki end

+ 1 - 2
shaders/Ssao.glsl

@@ -65,8 +65,7 @@ shared Vec3 s_scratch[WORKGROUP_SIZE.y][WORKGROUP_SIZE.x];
 // Get normal
 Vec3 readNormal(in Vec2 uv)
 {
-	Vec3 normal;
-	readNormalFromGBuffer(u_msRt, uv, normal);
+	Vec3 normal = readNormalFromGBuffer(u_msRt, uv);
 	normal = u_viewRotMat * normal;
 	return normal;
 }

+ 173 - 0
shaders/Ssr.glslp

@@ -0,0 +1,173 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki input const UVec2 FB_SIZE
+#pragma anki input const UVec2 WORKGROUP_SIZE
+#pragma anki input const U32 MAX_STEPS
+#pragma anki input const U32 LIGHT_BUFFER_MIP_COUNT
+
+#pragma anki start comp
+#include <shaders/Functions.glsl>
+#include <shaders/Pack.glsl>
+#include <shaders/glsl_cpp_common/Ssr.h>
+
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_gbufferRt1;
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_gbufferRt2;
+layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_depthRt;
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_lightBufferRt;
+
+layout(ANKI_IMAGE_BINDING(0, 0)) writeonly uniform image2D out_img;
+
+layout(ANKI_UBO_BINDING(0, 0), row_major) uniform u_
+{
+	SsrUniforms u_unis;
+};
+#define u_prevViewProjMatMulInvViewProjMat u_unis.m_prevViewProjMatMulInvViewProjMat
+#define u_near u_unis.m_nearPad3.x
+#define u_projMat u_unis.m_projMat
+#define u_invProjMat u_unis.m_invProjMat
+#define u_normalMat u_unis.m_normalMat
+
+// Note: All calculations in view space
+// It returns the UV coordinates of the reflection (xy) and the contrubution factor (z)
+Vec3 raymarch(Vec3 r, Vec3 n, Vec3 viewPos, Vec2 uv, F32 depth)
+{
+	Vec3 p0 = viewPos;
+
+	// Check for view facing reflections [sakibsaikia]
+	Vec3 viewDir = normalize(viewPos);
+	F32 cameraContribution = 1.0 - smoothstep(0.25, 0.5, dot(-viewDir, r));
+	if(cameraContribution <= 0.0)
+	{
+		return Vec3(0.0);
+	}
+
+	// Compute an end point p1. This point is supposed to fall in front of the near plane. Add a small padding to near
+	// to avoid having p1 touching the near plane.
+	Vec3 p1 = p0 + r * (-p0.z - (u_near + 0.1));
+
+	// Start point
+	Vec3 start = Vec3(uv, depth);
+
+	// Project end point
+	Vec4 end4 = u_projMat * Vec4(p1, 1.0);
+	Vec3 end = end4.xyz / end4.w;
+	end.xy = NDC_TO_UV(end.xy);
+
+	// Compute the ray and step size
+	Vec3 ray = end - start;
+	Vec2 texelDims = abs(ray.xy) * Vec2(FB_SIZE);
+	F32 stepSize = length(ray.xy) / max(texelDims.x, texelDims.y);
+	ray = normalize(ray);
+
+	// Compute step
+	const U32 BIG_STEP_SKIP = 32u;
+	U32 stepSkip = BIG_STEP_SKIP;
+
+	const U32 STEP_FRACTION = BIG_STEP_SKIP / (4u + 1u);
+	const U32 STEPS_ARR[4] = U32[](STEP_FRACTION, 4 * STEP_FRACTION, 2 * STEP_FRACTION, 3 * STEP_FRACTION);
+	U32 l = gl_GlobalInvocationID.x & 1u;
+	U32 j = gl_GlobalInvocationID.y & 1u;
+	U32 step = STEPS_ARR[l * 2u + j];
+
+	// Iterate
+	F32 finalContribution = 0.0;
+	Vec3 raySample;
+	ANKI_LOOP for(U32 iterations = 0u; iterations < MAX_STEPS; ++iterations)
+	{
+		raySample = start + ray * (F32(step) * stepSize);
+
+		// Check if it's out of the view
+		if(raySample.x <= 0.0 || raySample.y <= 0.0 || raySample.x >= 1.0 || raySample.y >= 1.0)
+		{
+			break;
+		}
+
+		F32 depth = textureLod(u_depthRt, raySample.xy, 0.0).r;
+
+		Bool hit = raySample.z - depth >= 0.0;
+		if(!hit)
+		{
+			step += stepSkip;
+		}
+		else if(stepSkip > 1)
+		{
+			step -= BIG_STEP_SKIP - 1u;
+			stepSkip = 1u;
+		}
+		else
+		{
+			// Found it
+
+			// Re-project previous frame
+			Vec4 v4 = u_prevViewProjMatMulInvViewProjMat * Vec4(UV_TO_NDC(raySample.xy), raySample.z, 1.0);
+			Vec2 ndc = v4.xy / v4.w;
+			raySample.xy = NDC_TO_UV(ndc);
+
+			// Compute the edge contribution
+			ndc = abs(ndc);
+			F32 screedEdgeContributionFactor = max(ndc.x, ndc.y);
+			screedEdgeContributionFactor = 1.0 - screedEdgeContributionFactor * screedEdgeContributionFactor;
+			screedEdgeContributionFactor = saturate(screedEdgeContributionFactor);
+			finalContribution = cameraContribution * screedEdgeContributionFactor;
+
+			break;
+		}
+	}
+
+	// Return the traced UV and the contribution factor
+	return Vec3(raySample.xy, finalContribution);
+}
+
+void main()
+{
+	if(gl_GlobalInvocationID.x >= FB_SIZE.x || gl_GlobalInvocationID.y >= FB_SIZE.y)
+	{
+		return;
+	}
+
+	Vec2 uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(FB_SIZE);
+
+	// Read part of the G-buffer
+	F32 roughness = readRoughnessFromGBuffer(u_gbufferRt1, uv);
+	Vec3 worldNormal = readNormalFromGBuffer(u_gbufferRt2, uv);
+
+	// Get depth
+	F32 depth = textureLod(u_depthRt, uv, 0.0).r;
+
+	// Get view pos
+	Vec4 viewPos4 = u_invProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
+	Vec3 viewPos = viewPos4.xyz / viewPos4.w;
+
+	// Compute refl vector
+	Vec3 viewDir = normalize(viewPos);
+	Vec3 viewNormal = u_normalMat * worldNormal;
+	Vec3 reflVec = reflect(viewDir, viewNormal);
+
+	// Raymatch
+	Vec3 ssr = raymarch(reflVec, viewNormal, viewPos, uv, depth);
+	Vec2 reflUv = ssr.xy;
+	F32 factor = ssr.z;
+
+	// Read the reflection
+	Vec3 reflColor;
+	if(factor > 0.0)
+	{
+		// Read the refl
+		F32 lod = F32(LIGHT_BUFFER_MIP_COUNT - 1u) * roughness;
+		reflColor = textureLod(u_lightBufferRt, reflUv, lod).rgb;
+		reflColor = clamp(reflColor, 0.0, FLT_MAX); // Fix the value just in case
+	}
+	else
+	{
+		reflColor = Vec3(0.0);
+	}
+
+	// Store to the image
+	imageStore(out_img, ivec2(gl_GlobalInvocationID.xy), Vec4(reflColor * factor, factor));
+}
+#pragma anki end

+ 26 - 0
shaders/glsl_cpp_common/Ssr.h

@@ -0,0 +1,26 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <shaders/glsl_cpp_common/Common.h>
+
+ANKI_BEGIN_NAMESPACE
+
+// Screen space reflections uniforms
+struct SsrUniforms
+{
+	Vec4 m_nearPad3;
+	Mat4 m_prevViewProjMatMulInvViewProjMat;
+	Mat4 m_projMat;
+	Mat4 m_invProjMat;
+#ifdef __cplusplus
+	Mat3x4 m_normalMat;
+#else
+	Mat3 m_normalMat;
+#endif
+};
+
+ANKI_END_NAMESPACE

+ 1 - 1
src/anki/Renderer.h

@@ -21,7 +21,7 @@
 #include <anki/renderer/LensFlare.h>
 #include <anki/renderer/TemporalAA.h>
 #include <anki/renderer/RenderQueue.h>
-#include <anki/renderer/Reflections.h>
+#include <anki/renderer/Ssr.h>
 #include <anki/renderer/Indirect.h>
 #include <anki/renderer/Dbg.h>
 #include <anki/renderer/Ssao.h>

+ 1 - 1
src/anki/gr/RenderGraph.cpp

@@ -17,7 +17,7 @@
 namespace anki
 {
 
-#define ANKI_DBG_RENDER_GRAPH 0
+#define ANKI_DBG_RENDER_GRAPH 1
 
 /// Contains some extra things for render targets.
 class RenderGraph::RT

+ 6 - 0
src/anki/gr/RenderGraph.h

@@ -286,6 +286,12 @@ public:
 	/// Add new producer dependency.
 	void newProducer(const RenderPassDependency& dep);
 
+	void newConsumerAndProducer(const RenderPassDependency& dep)
+	{
+		newConsumer(dep);
+		newProducer(dep);
+	}
+
 protected:
 	enum class Type : U8
 	{

+ 1 - 1
src/anki/gr/ShaderCompiler.cpp

@@ -250,7 +250,7 @@ static ANKI_USE_RESULT Error genSpirv(const ShaderCompiler::BuildContext& ctx, s
 	glslang::TShader shader(stage);
 	Array<const char*, 1> csrc = {{&ctx.m_src[0]}};
 	shader.setStrings(&csrc[0], 1);
-	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
+	shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0); // TODO: Chose the right version
 	if(!shader.parse(&GLSLANG_LIMITS, 100, false, messages))
 	{
 		ShaderCompiler::logShaderErrorCode(shader.getInfoLog(), ctx.m_src, ctx.m_alloc);

+ 1 - 1
src/anki/renderer/Common.h

@@ -35,8 +35,8 @@ class DownscaleBlur;
 class Volumetric;
 class DepthDownscale;
 class TemporalAA;
-class Reflections;
 class UiStage;
+class Ssr;
 
 class RenderingContext;
 class DebugDrawer;

+ 1 - 1
src/anki/renderer/FinalComposite.cpp

@@ -12,7 +12,7 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/Dbg.h>
 #include <anki/renderer/Ssao.h>
-#include <anki/renderer/Reflections.h>
+#include <anki/renderer/Ssr.h>
 #include <anki/renderer/DownscaleBlur.h>
 #include <anki/renderer/UiStage.h>
 #include <anki/util/Logger.h>

+ 0 - 55
src/anki/renderer/ForwardShading.cpp

@@ -52,7 +52,6 @@ Error ForwardShading::initInternal(const ConfigSet&)
 	m_fbDescr.bake();
 
 	ANKI_CHECK(initVol());
-	ANKI_CHECK(initUpscale());
 
 	return Error::NONE;
 }
@@ -75,25 +74,6 @@ Error ForwardShading::initVol()
 	return Error::NONE;
 }
 
-Error ForwardShading::initUpscale()
-{
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_upscale.m_noiseTex));
-
-	// Shader
-	ANKI_CHECK(getResourceManager().loadResource("shaders/ForwardShadingUpscale.glslp", m_upscale.m_prog));
-
-	ShaderProgramResourceConstantValueInitList<3> consts(m_upscale.m_prog);
-	consts.add("NOISE_TEX_SIZE", U32(m_upscale.m_noiseTex->getWidth()))
-		.add("SRC_SIZE", Vec2(m_r->getWidth() / FS_FRACTION, m_r->getHeight() / FS_FRACTION))
-		.add("FB_SIZE", Vec2(m_r->getWidth(), m_r->getWidth()));
-
-	const ShaderProgramResourceVariant* variant;
-	m_upscale.m_prog->getOrCreateVariant(consts.get(), variant);
-	m_upscale.m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
 void ForwardShading::drawVolumetric(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
@@ -126,41 +106,6 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, RenderPassWorkContext
 	cmdb->setDepthCompareOperation(CompareOperation::LESS);
 }
 
-void ForwardShading::drawUpscale(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	// **WARNING** Remember to update the consumers of the render pass that calls this method
-	Vec4* linearDepth = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
-	computeLinearizeDepthOptimal(
-		ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, linearDepth->x(), linearDepth->y());
-	linearDepth->z() = ctx.m_renderQueue->m_cameraFar;
-
-	rgraphCtx.bindTextureAndSampler(0,
-		0,
-		m_r->getGBuffer().getDepthRt(),
-		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
-		m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(
-		0, 1, m_r->getDepthDownscale().getHiZRt(), HIZ_HALF_DEPTH, m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_rt, m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(0,
-		3,
-		m_upscale.m_noiseTex->getGrTextureView(),
-		m_r->getTrilinearRepeatSampler(),
-		TextureUsageBit::SAMPLED_FRAGMENT);
-
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
-
-	cmdb->bindShaderProgram(m_upscale.m_grProg);
-	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-
-	drawQuad(cmdb);
-
-	// Restore state
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
-}
-
 void ForwardShading::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;

+ 0 - 9
src/anki/renderer/ForwardShading.h

@@ -61,14 +61,6 @@ private:
 		TextureResourcePtr m_noiseTex;
 	} m_vol;
 
-	class Upscale
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-		TextureResourcePtr m_noiseTex;
-	} m_upscale;
-
 	class
 	{
 	public:
@@ -78,7 +70,6 @@ private:
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initVol();
-	ANKI_USE_RESULT Error initUpscale();
 
 	/// A RenderPassWorkCallback.
 	static void runCallback(RenderPassWorkContext& rgraphCtx)

+ 147 - 57
src/anki/renderer/LightShading.cpp

@@ -12,7 +12,7 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/ForwardShading.h>
 #include <anki/renderer/DepthDownscale.h>
-#include <anki/renderer/Reflections.h>
+#include <anki/renderer/Ssr.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/collision/Functions.h>
@@ -71,12 +71,11 @@ Error LightShading::initInternal(const ConfigSet& config)
 	// Load shaders and programs
 	ANKI_CHECK(getResourceManager().loadResource("shaders/LightShading.glslp", m_prog));
 
-	ShaderProgramResourceConstantValueInitList<5> consts(m_prog);
+	ShaderProgramResourceConstantValueInitList<4> consts(m_prog);
 	consts.add("CLUSTER_COUNT_X", U32(m_clusterCounts[0]))
 		.add("CLUSTER_COUNT_Y", U32(m_clusterCounts[1]))
 		.add("CLUSTER_COUNT_Z", U32(m_clusterCounts[2]))
-		.add("CLUSTER_COUNT", U32(m_clusterCount))
-		.add("IR_MIPMAP_COUNT", U32(m_r->getIndirect().getReflectionTextureMipmapCount()));
+		.add("CLUSTER_COUNT", U32(m_clusterCount));
 
 	m_prog->getOrCreateVariant(consts.get(), m_progVariant);
 
@@ -90,6 +89,39 @@ Error LightShading::initInternal(const ConfigSet& config)
 	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
 	m_fbDescr.bake();
 
+	// Refl & indirect
+	{
+		ANKI_CHECK(getResourceManager().loadResource(
+			"shaders/LightShadingReflectionsAndIndirect.glslp", m_reflAndIndirect.m_prog));
+
+		ShaderProgramResourceConstantValueInitList<4> consts(m_reflAndIndirect.m_prog);
+		consts.add("CLUSTER_COUNT_X", U32(m_clusterCounts[0]))
+			.add("CLUSTER_COUNT_Y", U32(m_clusterCounts[1]))
+			.add("CLUSTER_COUNT_Z", U32(m_clusterCounts[2]))
+			.add("IR_MIPMAP_COUNT", U32(m_r->getIndirect().getReflectionTextureMipmapCount()));
+
+		const ShaderProgramResourceVariant* variant;
+		m_reflAndIndirect.m_prog->getOrCreateVariant(consts.get(), variant);
+		m_reflAndIndirect.m_grProg = variant->getProgram();
+	}
+
+	// FS upscale
+	{
+		ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_fs.m_noiseTex));
+
+		// Shader
+		ANKI_CHECK(getResourceManager().loadResource("shaders/ForwardShadingUpscale.glslp", m_fs.m_prog));
+
+		ShaderProgramResourceConstantValueInitList<3> consts(m_fs.m_prog);
+		consts.add("NOISE_TEX_SIZE", U32(m_fs.m_noiseTex->getWidth()))
+			.add("SRC_SIZE", Vec2(m_r->getWidth() / FS_FRACTION, m_r->getHeight() / FS_FRACTION))
+			.add("FB_SIZE", Vec2(m_r->getWidth(), m_r->getWidth()));
+
+		const ShaderProgramResourceVariant* variant;
+		m_fs.m_prog->getOrCreateVariant(consts.get(), variant);
+		m_fs.m_grProg = variant->getProgram();
+	}
+
 	return Error::NONE;
 }
 
@@ -116,35 +148,124 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 	const LightShadingResources& rsrc = m_runCtx.m_resources;
 
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindShaderProgram(m_progVariant->getProgram());
 
-	// Bind textures
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getGBuffer().getColorRt(0), m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(1), m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getGBuffer().getColorRt(2), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0,
-		3,
-		m_r->getGBuffer().getDepthRt(),
-		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
-		m_r->getNearestSampler());
+	// Do light shading
+	{
+		cmdb->bindShaderProgram(m_progVariant->getProgram());
+
+		// Bind textures
+		rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getGBuffer().getColorRt(0), m_r->getNearestSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(1), m_r->getNearestSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getGBuffer().getColorRt(2), m_r->getNearestSampler());
+		rgraphCtx.bindTextureAndSampler(0,
+			3,
+			m_r->getGBuffer().getDepthRt(),
+			TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+			m_r->getNearestSampler());
+
+		rgraphCtx.bindColorTextureAndSampler(0, 4, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+
+		// Bind uniforms
+		bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+		bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
+		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+
+		// Bind storage
+		bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
+		bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+
+		drawQuad(cmdb);
+	}
+
+	// Add the reflections & indirect
+	{
+		cmdb->bindShaderProgram(m_reflAndIndirect.m_grProg);
+
+		// Bind textures
+		rgraphCtx.bindColorTextureAndSampler(0, 4, m_r->getSsr().getRt(), m_r->getLinearSampler());
+		rgraphCtx.bindColorTextureAndSampler(
+			0, 5, m_r->getIndirect().getReflectionRt(), m_r->getTrilinearRepeatSampler());
+		rgraphCtx.bindColorTextureAndSampler(
+			0, 6, m_r->getIndirect().getIrradianceRt(), m_r->getTrilinearRepeatSampler());
+		cmdb->bindTextureAndSampler(0,
+			7,
+			m_r->getIndirect().getIntegrationLut(),
+			m_r->getIndirect().getIntegrationLutSampler(),
+			TextureUsageBit::SAMPLED_FRAGMENT);
+
+		// Bind uniforms
+		bindUniforms(cmdb, 0, 1, rsrc.m_probesToken);
+
+		// State & draw
+		cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
+		drawQuad(cmdb);
+	}
+
+	// Forward shading
+	{
+		// Bind textures
+		rgraphCtx.bindTextureAndSampler(0,
+			0,
+			m_r->getGBuffer().getDepthRt(),
+			TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+			m_r->getNearestSampler());
+		rgraphCtx.bindTextureAndSampler(
+			0, 1, m_r->getDepthDownscale().getHiZRt(), HIZ_HALF_DEPTH, m_r->getNearestSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getForwardShading().getRt(), m_r->getLinearSampler());
+		cmdb->bindTextureAndSampler(0,
+			3,
+			m_fs.m_noiseTex->getGrTextureView(),
+			m_r->getTrilinearRepeatSampler(),
+			TextureUsageBit::SAMPLED_FRAGMENT);
+
+		// Bind uniforms
+		Vec4* linearDepth = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
+		computeLinearizeDepthOptimal(
+			ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, linearDepth->x(), linearDepth->y());
+		linearDepth->z() = ctx.m_renderQueue->m_cameraFar;
+
+		// Other state & draw
+		cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
+		cmdb->bindShaderProgram(m_fs.m_grProg);
+		drawQuad(cmdb);
+	}
+
+	// Restore state
+	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
+}
 
-	rgraphCtx.bindColorTextureAndSampler(0, 4, m_r->getReflections().getRt(), m_r->getNearestSampler());
+void LightShading::populateRenderGraph(RenderingContext& ctx)
+{
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
-	rgraphCtx.bindColorTextureAndSampler(0, 5, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+	// Create RT
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
 
-	// Bind uniforms
-	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
-	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+	// Create pass
+	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Light Shading");
+
+	pass.setWork(runCallback, this, 0);
+	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {});
 
-	// Bind storage
-	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+	// Light shading
+	pass.newConsumerAndProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	pass.newConsumer({m_r->getGBuffer().getColorRt(0), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getGBuffer().getColorRt(1), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getGBuffer().getDepthRt(),
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
+	pass.newConsumer({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 
-	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
+	// Refl & indirect
+	pass.newConsumer({m_r->getSsr().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+	pass.newConsumer({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 
-	// Apply the forward shading result
-	m_r->getForwardShading().drawUpscale(ctx, rgraphCtx);
+	// For forward shading
+	pass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_HALF_DEPTH});
+	pass.newConsumer({m_r->getForwardShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 }
 
 void LightShading::updateCommonBlock(RenderingContext& ctx)
@@ -180,35 +301,4 @@ void LightShading::updateCommonBlock(RenderingContext& ctx)
 	blk->m_prevViewProjMatMulInvViewProjMat = ctx.m_prevViewProjMat * ctx.m_viewProjMatJitter.getInverse();
 }
 
-void LightShading::populateRenderGraph(RenderingContext& ctx)
-{
-	m_runCtx.m_ctx = &ctx;
-	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
-
-	// Create RT
-	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
-
-	// Create pass
-	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Light Shading");
-
-	pass.setWork(runCallback, this, 0);
-	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {});
-
-	pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-	pass.newConsumer({m_r->getGBuffer().getColorRt(0), TextureUsageBit::SAMPLED_FRAGMENT});
-	pass.newConsumer({m_r->getGBuffer().getColorRt(1), TextureUsageBit::SAMPLED_FRAGMENT});
-	pass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_FRAGMENT});
-	pass.newConsumer({m_r->getGBuffer().getDepthRt(),
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
-	pass.newConsumer({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
-	pass.newConsumer({m_r->getReflections().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
-
-	// For forward shading
-	pass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_HALF_DEPTH});
-	pass.newConsumer({m_r->getForwardShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
-
-	pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-}
-
 } // end namespace anki

+ 15 - 0
src/anki/renderer/LightShading.h

@@ -70,6 +70,21 @@ private:
 	U32 m_maxLightIds;
 	/// @}
 
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_reflAndIndirect; ///< Apply reflections drawcall.
+
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+		TextureResourcePtr m_noiseTex;
+	} m_fs; ///< Apply forward shading.
+
 	class
 	{
 	public:

+ 0 - 139
src/anki/renderer/Reflections.cpp

@@ -1,139 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/renderer/Reflections.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/renderer/GBuffer.h>
-#include <anki/renderer/Indirect.h>
-#include <anki/renderer/DepthDownscale.h>
-#include <anki/renderer/DownscaleBlur.h>
-#include <anki/renderer/RenderQueue.h>
-#include <anki/renderer/LightShading.h>
-#include <anki/misc/ConfigSet.h>
-
-namespace anki
-{
-
-Reflections::~Reflections()
-{
-}
-
-Error Reflections::init(const ConfigSet& cfg)
-{
-	Error err = initInternal(cfg);
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize reflection pass");
-	}
-	return err;
-}
-
-Error Reflections::initInternal(const ConfigSet& cfg)
-{
-	U32 width = m_r->getWidth();
-	U32 height = m_r->getHeight();
-	ANKI_R_LOGI("Initializing reflection pass (%ux%u)", width, height);
-
-	// Create RTs
-	m_rtDescr =
-		m_r->create2DRenderTargetDescription(width, height, LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT, "IndirectRes");
-	m_rtDescr.bake();
-
-	// Create shader
-	ANKI_CHECK(getResourceManager().loadResource("shaders/Reflections.glslp", m_prog));
-
-	ShaderProgramResourceConstantValueInitList<9> consts(m_prog);
-	consts.add("FB_SIZE", UVec2(width, height));
-	consts.add("WORKGROUP_SIZE", UVec2(m_workgroupSize[0], m_workgroupSize[1]));
-	consts.add("MAX_STEPS", U32(64));
-	consts.add("LIGHT_BUFFER_MIP_COUNT", U32(m_r->getDownscaleBlur().getMipmapCount()));
-	consts.add("HIZ_MIP_COUNT", U32(m_r->getDepthDownscale().getMipmapCount()));
-	consts.add("CLUSTER_COUNT_X", U32(cfg.getNumber("r.clusterSizeX")));
-	consts.add("CLUSTER_COUNT_Y", U32(cfg.getNumber("r.clusterSizeY")));
-	consts.add("CLUSTER_COUNT_Z", U32(cfg.getNumber("r.clusterSizeZ")));
-	consts.add("IR_MIPMAP_COUNT", U32(m_r->getIndirect().getReflectionTextureMipmapCount()));
-
-	ShaderProgramResourceMutationInitList<1> mutations(m_prog);
-	mutations.add("VARIANT", 0);
-
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(mutations.get(), consts.get(), variant);
-	m_grProg[0] = variant->getProgram();
-
-	mutations[0].m_value = 1;
-	m_prog->getOrCreateVariant(mutations.get(), consts.get(), variant);
-	m_grProg[1] = variant->getProgram();
-
-	return Error::NONE;
-}
-
-void Reflections::populateRenderGraph(RenderingContext& ctx)
-{
-	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
-	m_runCtx.m_ctx = &ctx;
-
-	// Create RTs
-	m_runCtx.m_indirectRt = rgraph.newRenderTarget(m_rtDescr);
-
-	// Create pass
-	ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("IndirectRes");
-	rpass.setWork(runCallback, this, 0);
-
-	rpass.newConsumer({m_runCtx.m_indirectRt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
-	rpass.newConsumer({m_r->getGBuffer().getColorRt(0), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getGBuffer().getColorRt(1), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE});
-
-	rpass.newConsumer({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_COMPUTE});
-	rpass.newConsumer({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_COMPUTE});
-
-	rpass.newProducer({m_runCtx.m_indirectRt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
-}
-
-void Reflections::run(RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	cmdb->bindShaderProgram(m_grProg[m_r->getFrameCount() & 1]);
-
-	// Bind textures
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getGBuffer().getColorRt(0), m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(1), m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getGBuffer().getColorRt(2), m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(0,
-		3,
-		m_r->getGBuffer().getDepthRt(),
-		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
-		m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 4, m_r->getDepthDownscale().getHiZRt(), m_r->getNearestNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 5, m_r->getDownscaleBlur().getRt(), m_r->getTrilinearRepeatSampler());
-
-	rgraphCtx.bindColorTextureAndSampler(0, 6, m_r->getIndirect().getReflectionRt(), m_r->getTrilinearRepeatSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 7, m_r->getIndirect().getIrradianceRt(), m_r->getTrilinearRepeatSampler());
-	cmdb->bindTextureAndSampler(0,
-		8,
-		m_r->getIndirect().getIntegrationLut(),
-		m_r->getIndirect().getIntegrationLutSampler(),
-		TextureUsageBit::SAMPLED_COMPUTE);
-
-	// Bind image
-	rgraphCtx.bindImage(0, 0, m_runCtx.m_indirectRt, TextureSubresourceInfo());
-
-	// Bind uniforms
-	const LightShadingResources& rsrc = m_r->getLightShading().getResources();
-	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
-	bindUniforms(cmdb, 0, 1, rsrc.m_probesToken);
-	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
-
-	// Dispatch
-	const U sizeX = (m_r->getWidth() + m_workgroupSize[0] - 1) / m_workgroupSize[0];
-	const U sizeY = (m_r->getHeight() + m_workgroupSize[1] - 1) / m_workgroupSize[1];
-	cmdb->dispatchCompute(sizeX / 2, sizeY, 1);
-}
-
-} // end namespace anki

+ 4 - 4
src/anki/renderer/Renderer.cpp

@@ -25,8 +25,8 @@
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/TemporalAA.h>
-#include <anki/renderer/Reflections.h>
 #include <anki/renderer/UiStage.h>
+#include <anki/renderer/Ssr.h>
 
 namespace anki
 {
@@ -138,8 +138,8 @@ Error Renderer::initInternal(const ConfigSet& config)
 	m_downscale.reset(getAllocator().newInstance<DownscaleBlur>(this));
 	ANKI_CHECK(m_downscale->init(config));
 
-	m_refl.reset(m_alloc.newInstance<Reflections>(this));
-	ANKI_CHECK(m_refl->init(config));
+	m_ssr.reset(m_alloc.newInstance<Ssr>(this));
+	ANKI_CHECK(m_ssr->init(config));
 
 	m_tonemapping.reset(getAllocator().newInstance<Tonemapping>(this));
 	ANKI_CHECK(m_tonemapping->init(config));
@@ -277,7 +277,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_gbufferPost->populateRenderGraph(ctx);
 	m_lensFlare->populateRenderGraph(ctx);
 	m_forwardShading->populateRenderGraph(ctx);
-	m_refl->populateRenderGraph(ctx);
+	m_ssr->populateRenderGraph(ctx);
 	m_lightShading->populateRenderGraph(ctx);
 	m_temporalAA->populateRenderGraph(ctx);
 	m_downscale->populateRenderGraph(ctx);

+ 5 - 5
src/anki/renderer/Renderer.h

@@ -152,14 +152,14 @@ public:
 		return *m_lensFlare;
 	}
 
-	Reflections& getReflections()
+	UiStage& getUiStage()
 	{
-		return *m_refl;
+		return *m_uiStage;
 	}
 
-	UiStage& getUiStage()
+	Ssr& getSsr()
 	{
-		return *m_uiStage;
+		return *m_ssr;
 	}
 
 	U32 getWidth() const
@@ -349,7 +349,7 @@ private:
 	UniquePtr<ShadowMapping> m_shadowMapping; ///< Shadow mapping.
 	UniquePtr<GBuffer> m_gbuffer; ///< Material rendering stage
 	UniquePtr<GBufferPost> m_gbufferPost;
-	UniquePtr<Reflections> m_refl;
+	UniquePtr<Ssr> m_ssr;
 	UniquePtr<LightShading> m_lightShading; ///< Illumination rendering stage
 	UniquePtr<DepthDownscale> m_depth;
 	UniquePtr<ForwardShading> m_forwardShading; ///< Forward shading.

+ 110 - 0
src/anki/renderer/Ssr.cpp

@@ -0,0 +1,110 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/renderer/Ssr.h>
+#include <anki/renderer/Renderer.h>
+#include <anki/renderer/GBuffer.h>
+#include <anki/renderer/DepthDownscale.h>
+#include <anki/renderer/DownscaleBlur.h>
+#include <anki/renderer/RenderQueue.h>
+#include <anki/misc/ConfigSet.h>
+#include <shaders/glsl_cpp_common/Ssr.h>
+
+namespace anki
+{
+
+Ssr::~Ssr()
+{
+}
+
+Error Ssr::init(const ConfigSet& cfg)
+{
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize reflection pass");
+	}
+	return err;
+}
+
+Error Ssr::initInternal(const ConfigSet& cfg)
+{
+	U32 width = m_r->getWidth() / 2u;
+	U32 height = m_r->getHeight() / 2u;
+	ANKI_R_LOGI("Initializing SSR pass (%ux%u)", width, height);
+
+	// Create RTs
+	m_rtDescr = m_r->create2DRenderTargetDescription(width, height, Format::R16G16B16A16_SFLOAT, "SSR");
+	m_rtDescr.bake();
+
+	// Create shader
+	ANKI_CHECK(getResourceManager().loadResource("shaders/Ssr.glslp", m_prog));
+
+	ShaderProgramResourceConstantValueInitList<4> consts(m_prog);
+	consts.add("FB_SIZE", UVec2(width, height));
+	consts.add("WORKGROUP_SIZE", UVec2(m_workgroupSize[0], m_workgroupSize[1]));
+	consts.add("MAX_STEPS", U32(64));
+	consts.add("LIGHT_BUFFER_MIP_COUNT", U32(m_r->getDownscaleBlur().getMipmapCount()));
+
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(consts.get(), variant);
+	m_grProg = variant->getProgram();
+
+	return Error::NONE;
+}
+
+void Ssr::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	m_runCtx.m_ctx = &ctx;
+
+	// Create RTs
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	// Create pass
+	ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("SSR");
+	rpass.setWork(runCallback, this, 0);
+
+	rpass.newConsumerAndProducer({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
+	rpass.newConsumer({m_r->getGBuffer().getColorRt(1), TextureUsageBit::SAMPLED_COMPUTE});
+	rpass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE});
+
+	TextureSubresourceInfo hizSubresource; // Only first mip
+	rpass.newConsumer({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_COMPUTE, hizSubresource});
+
+	rpass.newConsumer({m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE});
+}
+
+void Ssr::run(RenderPassWorkContext& rgraphCtx)
+{
+	RenderingContext& ctx = *m_runCtx.m_ctx;
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	cmdb->bindShaderProgram(m_grProg);
+
+	// Bind textures
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getGBuffer().getColorRt(1), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getGBuffer().getColorRt(2), m_r->getLinearSampler());
+
+	TextureSubresourceInfo hizSubresource; // Only first mip
+	rgraphCtx.bindTextureAndSampler(0, 2, m_r->getDepthDownscale().getHiZRt(), hizSubresource, m_r->getLinearSampler());
+
+	rgraphCtx.bindColorTextureAndSampler(0, 3, m_r->getDownscaleBlur().getRt(), m_r->getTrilinearRepeatSampler());
+
+	// Bind image
+	rgraphCtx.bindImage(0, 0, m_runCtx.m_rt, TextureSubresourceInfo());
+
+	// Bind uniforms
+	SsrUniforms* unis = allocateAndBindUniforms<SsrUniforms*>(sizeof(SsrUniforms), cmdb, 0, 0);
+	unis->m_nearPad3 = Vec4(ctx.m_renderQueue->m_cameraNear);
+	unis->m_prevViewProjMatMulInvViewProjMat = ctx.m_prevViewProjMat * ctx.m_viewProjMatJitter.getInverse();
+	unis->m_projMat = ctx.m_projMatJitter;
+	unis->m_invProjMat = ctx.m_projMatJitter.getInverse();
+	unis->m_normalMat = Mat3x4(ctx.m_renderQueue->m_viewMatrix.getRotationPart());
+
+	// Dispatch
+	dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_r->getWidth() / 2u, m_r->getHeight() / 2u);
+}
+
+} // end namespace anki

+ 8 - 8
src/anki/renderer/Reflections.h → src/anki/renderer/Ssr.h

@@ -13,16 +13,16 @@ namespace anki
 /// @addtogroup renderer
 /// @{
 
-/// Reflections pass. It does SSR and probe reflections and it conbines them with the irradiance as well.
-class Reflections : public RendererObject
+/// Screen space reflections.
+class Ssr : public RendererObject
 {
 anki_internal:
-	Reflections(Renderer* r)
+	Ssr(Renderer* r)
 		: RendererObject(r)
 	{
 	}
 
-	~Reflections();
+	~Ssr();
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
@@ -31,12 +31,12 @@ anki_internal:
 
 	RenderTargetHandle getRt() const
 	{
-		return m_runCtx.m_indirectRt;
+		return m_runCtx.m_rt;
 	}
 
 private:
 	ShaderProgramResourcePtr m_prog;
-	Array<ShaderProgramPtr, 2> m_grProg;
+	ShaderProgramPtr m_grProg;
 
 	RenderTargetDescription m_rtDescr;
 
@@ -45,7 +45,7 @@ private:
 	class
 	{
 	public:
-		RenderTargetHandle m_indirectRt;
+		RenderTargetHandle m_rt;
 		RenderingContext* m_ctx ANKI_DBG_NULLIFY;
 	} m_runCtx;
 
@@ -53,7 +53,7 @@ private:
 
 	static void runCallback(RenderPassWorkContext& rgraphCtx)
 	{
-		static_cast<Reflections*>(rgraphCtx.m_userData)->run(rgraphCtx);
+		static_cast<Ssr*>(rgraphCtx.m_userData)->run(rgraphCtx);
 	}
 
 	void run(RenderPassWorkContext& rgraphCtx);