Browse Source

Renderer: Fix a serious bug in irradiance probes

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
bcfd18a180

+ 9 - 0
shaders/Functions.glsl

@@ -174,4 +174,13 @@ vec4 bilateralUpsample(
 	return sum / normalize;
 }
 
+vec3 getCubemapDirection(vec2 norm, uint faceIdx)
+{
+	vec3 zDir = vec3((faceIdx <= 1u) ? 1 : 0, (faceIdx & 2u) >> 1u, (faceIdx & 4u) >> 2u);
+	zDir *= (((faceIdx & 1u) == 1u) ? -1.0 : 1.0);
+	vec3 yDir = (faceIdx == 2u) ? vec3(0.0, 0.0, 1.0) : (faceIdx == 3u) ? vec3(0.0, 0.0, -1.0) : vec3(0.0, -1.0, 0.0);
+	vec3 xDir = cross(zDir, yDir);
+	return normalize(norm.x * xDir + norm.y * yDir + zDir);
+}
+
 #endif

+ 12 - 18
shaders/Irradiance.frag.glsl

@@ -5,7 +5,7 @@
 
 // Compute the irradiance given an environment map
 
-#include "shaders/Common.glsl"
+#include "shaders/Functions.glsl"
 
 const float INDIRECT_BUMP = 2.5; // A sort of hack
 
@@ -21,13 +21,6 @@ layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 	uvec4 u_faceIdxArrayIdxPad2;
 };
 
-const mat3 CUBE_ROTATIONS[6] = mat3[](mat3(vec3(0.0, 0.0, -1.0), vec3(0.0, -1.0, 0.0), vec3(-1.0, 0.0, 0.0)),
-	mat3(vec3(0.0, 0.0, 1.0), vec3(0.0, -1.0, 0.0), vec3(1.0, 0.0, 0.0)),
-	mat3(vec3(1.0, 0.0, -0.0), vec3(0.0, -0.0, 1.0), vec3(0.0, -1.0, -0.0)),
-	mat3(vec3(1.0, 0.0, -0.0), vec3(0.0, -0.0, -1.0), vec3(-0.0, 1.0, -0.0)),
-	mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, -1.0)),
-	mat3(vec3(-1.0, -0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0)));
-
 // Integrate the environment map to compute the irradiance for a single fragment
 void main()
 {
@@ -35,11 +28,10 @@ void main()
 	float texArrIdx = float(u_faceIdxArrayIdxPad2.y);
 
 	// Get the r coordinate of the current face and fragment
-	vec2 ndc = in_uv * 2.0 - 1.0;
-	vec3 ri = CUBE_ROTATIONS[face] * normalize(vec3(ndc, -1.0));
+	vec3 ri = getCubemapDirection(UV_TO_NDC(in_uv), face);
 
 	vec3 outCol = vec3(0.0);
-	float weight = 0.0;
+	float weight = EPSILON;
 
 	// For all the faces and texels of the environment map calculate a color sum
 	for(uint f = 0u; f < 6u; ++f)
@@ -49,17 +41,19 @@ void main()
 			for(uint j = 0u; j < CUBEMAP_SIZE; ++j)
 			{
 				vec2 uv = vec2(j, i) / float(CUBEMAP_SIZE);
-				vec2 ndc = uv * 2.0 - 1.0;
-				vec3 r = CUBE_ROTATIONS[f] * normalize(vec3(ndc, -1.0));
+				vec3 r = getCubemapDirection(UV_TO_NDC(uv), f);
 
-				vec3 col = texture(u_envTex, vec4(r, texArrIdx)).rgb;
+				float lambert = dot(r, ri);
+				if(lambert > 0.0)
+				{
+					vec3 col = textureLod(u_envTex, vec4(r, texArrIdx), 0.0).rgb;
 
-				float lambert = max(0.0, dot(r, ri));
-				outCol += col * lambert * INDIRECT_BUMP;
-				weight += lambert;
+					outCol += col * lambert;
+					weight += lambert;
+				}
 			}
 		}
 	}
 
-	out_color = outCol / max(EPSILON, weight);
+	out_color = (outCol * 4.0 * PI) / weight;
 }

+ 30 - 34
shaders/Is.frag.glsl

@@ -68,13 +68,8 @@ void appendDecalColors(in Decal decal, in vec3 fragPos, inout vec3 diffuseColor,
 	roughness = mix(roughness, r, dcol.a * decal.blendFactors[1]);
 }
 
-void readIndirect(in uint idxOffset,
-	in vec3 posVSpace,
-	in vec3 r,
-	in vec3 n,
-	in float lod,
-	out vec3 specIndirect,
-	out vec3 diffIndirect)
+void readIndirect(
+	in uint idxOffset, in vec3 pos, in vec3 r, in vec3 n, in float lod, out vec3 specIndirect, out vec3 diffIndirect)
 {
 	specIndirect = vec3(0.0);
 	diffIndirect = vec3(0.0);
@@ -89,10 +84,10 @@ void readIndirect(in uint idxOffset,
 		vec3 center = probe.positionRadiusSq.xyz;
 
 		// Get distance from the center of the probe
-		vec3 f = posVSpace - center;
+		vec3 f = pos - center;
 
 		// Cubemap UV in view space
-		vec3 uv = computeCubemapVecAccurate(r, R2, f, u_lightingUniforms.invViewRotation);
+		vec3 uv = computeCubemapVecAccurate(r, R2, f);
 
 		// Read!
 		float cubemapIndex = probe.cubemapIndexPad3.x;
@@ -106,26 +101,12 @@ void readIndirect(in uint idxOffset,
 		// Same as: specIndirect = c * (1.0 - factor) + specIndirect * factor
 
 		// Do the same for diffuse
-		uv = computeCubemapVecCheap(n, R2, f, u_lightingUniforms.invViewRotation);
+		uv = computeCubemapVecCheap(n, R2, f);
 		vec3 id = texture(u_irradianceTex, vec4(uv, cubemapIndex)).rgb;
 		diffIndirect = mix(id, diffIndirect, factor);
 	}
 }
 
-float readSsao(float depth, vec2 ndc)
-{
-	vec4 worldPos4 = u_lightingUniforms.invViewProjMat * vec4(ndc, UV_TO_NDC(depth), 1.0);
-	worldPos4 = worldPos4 / worldPos4.w;
-
-	// Project to get old ndc
-	vec4 oldNdc4 = u_lightingUniforms.prevViewProjMat * worldPos4;
-	vec2 oldNdc = oldNdc4.xy / oldNdc4.w;
-
-	vec2 oldUv = NDC_TO_UV(oldNdc);
-
-	return texture(u_ssaoTex, oldUv).r;
-}
-
 void main()
 {
 	float depth = texture(u_msDepthRt, in_uv, 0.0).r;
@@ -157,8 +138,22 @@ void main()
 	emission = gbuffer.emission;
 
 	// Get SSAO
-	float ssao = readSsao(depth, ndc);
-	diffCol *= ssao;
+	vec3 worldPos;
+
+	{
+		vec4 worldPos4 = u_lightingUniforms.invViewProjMat * vec4(ndc, UV_TO_NDC(depth), 1.0);
+		worldPos4 = worldPos4 / worldPos4.w;
+		worldPos = worldPos4.xyz;
+
+		// Project to get old ndc
+		vec4 oldNdc4 = u_lightingUniforms.prevViewProjMat * worldPos4;
+		vec2 oldNdc = oldNdc4.xy / oldNdc4.w;
+
+		vec2 oldUv = NDC_TO_UV(oldNdc);
+
+		float ssao = texture(u_ssaoTex, oldUv).r;
+		diffCol *= ssao;
+	}
 
 	// Get counts and offsets
 	uint clusterIdx =
@@ -231,20 +226,21 @@ void main()
 
 #if INDIRECT_ENABLED
 	vec3 eye = -viewDir;
-	vec3 r = reflect(eye, normal);
-	float reflLod = float(IR_MIPMAP_COUNT) * roughness;
 
-	vec3 specIndirect, diffIndirect;
-	readIndirect(idxOffset, fragPos, r, normal, reflLod, specIndirect, diffIndirect);
+	vec3 worldEye = u_lightingUniforms.invViewRotation * eye;
+	vec3 worldNormal = u_lightingUniforms.invViewRotation * normal;
+	vec3 worldR = reflect(worldEye, worldNormal);
 
-	diffIndirect *= diffCol;
+	float reflLod = float(IR_MIPMAP_COUNT) * roughness;
 
-	// Finalize the indirect specular
 	float ndotv = dot(normal, viewDir);
 	vec2 envBRDF = texture(u_integrationLut, vec2(roughness, ndotv)).xy;
-	specIndirect = specIndirect * (specCol * envBRDF.x + envBRDF.y);
+	vec3 specIndirectTerm = specCol * envBRDF.x + envBRDF.y;
+
+	vec3 specIndirect, diffIndirect;
+	readIndirect(idxOffset, worldPos, worldR, worldNormal, reflLod, specIndirect, diffIndirect);
 
-	out_color += specIndirect + diffIndirect;
+	out_color += specIndirect * specIndirectTerm + diffIndirect * diffCol;
 #endif
 
 #if 0

+ 4 - 7
shaders/LightFunctions.glsl

@@ -162,7 +162,7 @@ float computeShadowFactorOmni(
 
 // Compute the cubemap texture lookup vector given the reflection vector (r) the radius squared of the probe (R2) and
 // the frag pos in sphere space (f)
-vec3 computeCubemapVecAccurate(in vec3 r, in float R2, in vec3 f, in mat3 invViewRotation)
+vec3 computeCubemapVecAccurate(in vec3 r, in float R2, in vec3 f)
 {
 	// Compute the collision of the r to the inner part of the sphere
 	// From now on we work on the sphere's space
@@ -180,16 +180,13 @@ vec3 computeCubemapVecAccurate(in vec3 r, in float R2, in vec3 f, in mat3 invVie
 	float sq = sqrt(R2 - pp);
 	vec3 x = p + sq * r;
 
-	// Rotate UV to move it to world space
-	vec3 uv = invViewRotation * x;
-
-	return uv;
+	return x;
 }
 
 // Cheap version of computeCubemapVecAccurate
-vec3 computeCubemapVecCheap(in vec3 r, in float R2, in vec3 f, in mat3 invViewRotation)
+vec3 computeCubemapVecCheap(in vec3 r, in float R2, in vec3 f)
 {
-	return invViewRotation * r;
+	return r;
 }
 
 #endif

+ 2 - 2
shaders/Ssao.frag.glsl

@@ -9,8 +9,8 @@
 #include "shaders/Functions.glsl"
 
 const vec2 KERNEL[SAMPLES] = KERNEL_ARRAY; // This will be appended in C++
-const float BIAS = 0.3;
-const float STRENGTH = 1.5;
+const float BIAS = 0.2;
+const float STRENGTH = 1.1;
 
 layout(location = 0) in vec2 in_uv;
 

+ 4 - 0
src/anki/renderer/Ir.cpp

@@ -670,6 +670,10 @@ Error Ir::renderReflection(RenderingContext& ctx, SceneNode& node, U cubemapIdx)
 	{
 		ANKI_CHECK(runMs(ctx, *frustumComponents[i], cubemapIdx, i));
 		runIs(ctx, *frustumComponents[i], cubemapIdx, i);
+	}
+
+	for(U i = 0; i < 6; ++i)
+	{
 		computeIrradiance(ctx, cubemapIdx, i);
 	}
 

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

@@ -92,7 +92,7 @@ private:
 		Array<FaceInfo, 6> m_faces;
 	};
 
-	static const U IRRADIANCE_TEX_SIZE = 32;
+	static const U IRRADIANCE_TEX_SIZE = 16;
 	static const U MAX_PROBE_RENDERS_PER_FRAME = 1;
 
 	U16 m_cubemapArrSize = 0;

+ 8 - 7
src/anki/renderer/LightBin.cpp

@@ -137,6 +137,12 @@ public:
 		m_probeRadius = r / F32(MAX_PROBE_RADIUS) * F32(MAX_U16);
 	}
 
+	Bool operator<(const ClusterProbeIndex& b) const
+	{
+		ANKI_ASSERT(m_probeRadius > 0.0 && b.m_probeRadius > 0.0);
+		return m_probeRadius < b.m_probeRadius;
+	}
+
 private:
 	static const U MAX_PROBE_RADIUS = 1000;
 	U16 m_index;
@@ -194,12 +200,7 @@ public:
 		const U probeCount = m_probeCount.get();
 		if(probeCount > 1)
 		{
-			std::sort(m_probeIds.getBegin(),
-				m_probeIds.getBegin() + probeCount,
-				[](const ClusterProbeIndex& a, const ClusterProbeIndex& b) {
-					ANKI_ASSERT(a.getProbeRadius() > 0.0 && b.getProbeRadius() > 0.0);
-					return a.getProbeRadius() < b.getProbeRadius();
-				});
+			std::sort(m_probeIds.getBegin(), m_probeIds.getBegin() + probeCount);
 		}
 
 		const U decalCount = m_decalCount.get();
@@ -797,7 +798,7 @@ void LightBin::writeAndBinProbe(
 
 	// Write it
 	ShaderProbe probe;
-	probe.m_pos = (camFrc.getViewMatrix() * reflc.getPosition().xyz1()).xyz();
+	probe.m_pos = reflc.getPosition().xyz();
 	probe.m_radiusSq = reflc.getRadius() * reflc.getRadius();
 	probe.m_cubemapIndex = reflc.getTextureArrayIndex();