Просмотр исходного кода

Merge pull request #36 from godlikepanos/box_refl_probes

Make reflection probes AABBs
Panagiotis Christopoulos Charitos 7 лет назад
Родитель
Сommit
96ce34210b

+ 18 - 198
samples/sponza/assets/scene.lua

@@ -5,15 +5,6 @@ local node
 local inst
 local lcomp
 
-node = scene:newSectorNode("Cube0", "assets/Cube.ankimesh")
-trf = Transform.new()
-trf:setOrigin(Vec4.new(0, 10.6707, 0, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
 node = scene:newParticleEmitterNode("particles0", "assets/fire.ankipart")
 trf = Transform.new()
 trf:setOrigin(Vec4.new(-10.7851, 2.17929, 2.47133, 0))
@@ -86,252 +77,81 @@ trf:setRotation(rot)
 trf:setScale(0.259678)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe0", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(18.0683, 2.49039, 6.66315, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe1", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(8.05931, 2.49039, 6.66315, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe2", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.43904, 2.49039, 6.66315, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe3", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-11.1261, 2.49039, 6.66315, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe4", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.0274, 2.49039, 6.66315, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe5", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.0274, 2.49039, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe6", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-11.1261, 2.49039, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe7", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.43904, 2.49039, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe8", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(8.05931, 2.49039, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe9", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(18.0683, 2.49039, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe10", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(18.9426, 2.49039, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe11", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(8.47687, 2.49039, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe12", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.45483, 2.49039, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe13", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-11.5838, 2.49039, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe14", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.8456, 2.49039, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe15", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.8456, 11.1297, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe16", 7.13881)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(18.9426, 11.1297, -0.625299, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe17", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(18.0683, 11.1297, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe18", 6.82732)
-trf = Transform.new()
-trf:setOrigin(Vec4.new(8.05931, 11.1297, -8.62114, 0))
-rot = Mat3x4.new()
-rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
-trf:setRotation(rot)
-trf:setScale(1)
-node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
-
-node = scene:newReflectionProbeNode("reflprobe19", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe0", Vec4.new(-24.8339, -3.89695, -4.12719, 0), Vec4.new(24.8339, 3.89695, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.43904, 11.1297, -8.62114, 0))
+trf:setOrigin(Vec4.new(-1.00211, 3.65293, -7.63218, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe20", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe1", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-11.1261, 11.1297, -8.62114, 0))
+trf:setOrigin(Vec4.new(-1.00211, 11.4761, -7.63218, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe21", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe2", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.0274, 11.1297, -8.62114, 0))
+trf:setOrigin(Vec4.new(-1.00211, 19.8704, -7.63218, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe22", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe3", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-19.0274, 11.1297, 6.66315, 0))
+trf:setOrigin(Vec4.new(-1.00211, 19.8704, -0.0445416, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe23", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe4", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-11.1261, 11.1297, 6.66315, 0))
+trf:setOrigin(Vec4.new(-1.00211, 11.4761, -0.0445416, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe24", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe5", Vec4.new(-24.8339, -3.89695, -4.12719, 0), Vec4.new(24.8339, 3.89695, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.43904, 11.1297, 6.66315, 0))
+trf:setOrigin(Vec4.new(-1.00211, 3.65293, -0.0445416, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe25", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe6", Vec4.new(-24.8339, -3.89695, -4.12719, 0), Vec4.new(24.8339, 3.89695, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(8.05931, 11.1297, 6.66315, 0))
+trf:setOrigin(Vec4.new(-1.00211, 3.65293, 6.48553, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe26", 6.82732)
+node = scene:newReflectionProbeNode("reflprobe7", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(18.0683, 11.1297, 6.66315, 0))
+trf:setOrigin(Vec4.new(-1.00211, 11.4761, 6.48553, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)
 trf:setScale(1)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
-node = scene:newReflectionProbeNode("reflprobe27", 35.9169)
+node = scene:newReflectionProbeNode("reflprobe8", Vec4.new(-24.8339, -4.2659, -4.12719, 0), Vec4.new(24.8339, 4.2659, 4.12719, 0))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(0, 9.57409, -0.625299, 0))
+trf:setOrigin(Vec4.new(-1.00211, 19.8704, 6.48553, 0))
 rot = Mat3x4.new()
 rot:setAll(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0)
 trf:setRotation(rot)

+ 10 - 3
shaders/ForwardShadingUpscale.ankiprog

@@ -23,7 +23,8 @@ http://www.anki3d.org/LICENSE
 #include <shaders/Common.glsl>
 #include <shaders/Functions.glsl>
 
-#define BLUE_NOISE 1
+#define EXPENSIVE_PASS 0
+#define BLUE_NOISE 0
 
 layout(location = 0) in Vec2 in_uv;
 
@@ -38,13 +39,19 @@ layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2DArray u_noiseTex;
 
 layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
 {
-	Vec4 u_linearizeCfPad2;
+	Vec4 u_linearizeCfFarPad1;
 };
 
 void main()
 {
+#if EXPENSIVE_PASS
 	out_color =
-		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_fsRt, 1.0 / Vec2(SRC_SIZE), in_uv, u_linearizeCfPad2.xy);
+		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_fsRt, 1.0 / Vec2(SRC_SIZE), in_uv, u_linearizeCfFarPad1.xy);
+#else
+	const F32 thresholdMeters = 5.0 / 100.0; // 5 centimeters
+	F32 threshold = thresholdMeters / u_linearizeCfFarPad1.z;
+	out_color = nearestDepthUpscale(in_uv, u_depthFullTex, u_depthHalfTex, u_fsRt, u_linearizeCfFarPad1.xy, threshold);
+#endif
 
 #if BLUE_NOISE
 	Vec3 blueNoise = texture(u_noiseTex, Vec3(FB_SIZE / Vec2(NOISE_TEX_SIZE) * in_uv, 0.0), 0.0).rgb;

+ 28 - 4
shaders/Functions.glsl

@@ -46,6 +46,12 @@ F32 linearizeDepthOptimal(in F32 depth, in F32 a, in F32 b)
 	return 1.0 / (a + b / depth);
 }
 
+// This is the optimal linearizeDepth where a=(n-f)/n and b=f/n
+Vec4 linearizeDepthOptimal(in Vec4 depths, in F32 a, in F32 b)
+{
+	return 1.0 / (a + b / depths);
+}
+
 // Project a vector by knowing only the non zero values of a perspective matrix
 Vec4 projectPerspective(in Vec4 vec, in F32 m00, in F32 m11, in F32 m22, in F32 m23)
 {
@@ -78,17 +84,22 @@ F32 rand(Vec2 n)
 	return 0.5 + 0.5 * fract(sin(dot(n, Vec2(12.9898, 78.233))) * 43758.5453);
 }
 
-Vec3 nearestDepthUpscale(Vec2 uv, sampler2D depthFull, sampler2D depthHalf, sampler2D colorTex, F32 depthThreshold)
+Vec4 nearestDepthUpscale(
+	Vec2 uv, sampler2D depthFull, sampler2D depthHalf, sampler2D colorTex, Vec2 linearDepthCf, F32 depthThreshold)
 {
 	F32 fullDepth = texture(depthFull, uv).r;
+	fullDepth = linearizeDepthOptimal(fullDepth, linearDepthCf.x, linearDepthCf.y);
+
 	Vec4 halfDepths = textureGather(depthHalf, uv, 0);
+	halfDepths = linearizeDepthOptimal(halfDepths, linearDepthCf.x, linearDepthCf.y);
+
 	Vec4 diffs = abs(Vec4(fullDepth) - halfDepths);
-	Vec3 color;
+	Vec4 color;
 
 	if(all(lessThan(diffs, Vec4(depthThreshold))))
 	{
 		// No major discontinuites, sample with bilinear
-		color = texture(colorTex, uv).rgb;
+		color = texture(colorTex, uv);
 	}
 	else
 	{
@@ -96,6 +107,7 @@ Vec3 nearestDepthUpscale(Vec2 uv, sampler2D depthFull, sampler2D depthHalf, samp
 		Vec4 r = textureGather(colorTex, uv, 0);
 		Vec4 g = textureGather(colorTex, uv, 1);
 		Vec4 b = textureGather(colorTex, uv, 2);
+		Vec4 a = textureGather(colorTex, uv, 3);
 
 		F32 minDiff = diffs.x;
 		U32 comp = 0;
@@ -117,7 +129,7 @@ Vec3 nearestDepthUpscale(Vec2 uv, sampler2D depthFull, sampler2D depthHalf, samp
 			comp = 3;
 		}
 
-		color = Vec3(r[comp], g[comp], b[comp]);
+		color = Vec4(r[comp], g[comp], b[comp], a[comp]);
 	}
 
 	return color;
@@ -354,4 +366,16 @@ F32 cubeCoordSolidAngle(Vec2 norm, F32 cubeFaceSize)
 	return areaElement(v0.x, v0.y) - areaElement(v0.x, v1.y) - areaElement(v1.x, v0.y) + areaElement(v1.x, v1.y);
 }
 
+/// Intersect a ray against an AABB. The ray is inside the AABB. The function returns the distance 'a' where the
+/// intersection point is rayOrigin + rayDir * a
+/// https://community.arm.com/graphics/b/blog/posts/reflections-based-on-local-cubemaps-in-unity
+F32 rayAabbIntersectionInside(Vec3 rayOrigin, Vec3 rayDir, Vec3 aabbMin, Vec3 aabbMax)
+{
+	Vec3 intersectMaxPointPlanes = (aabbMax - rayOrigin) / rayDir;
+	Vec3 intersectMinPointPlanes = (aabbMin - rayOrigin) / rayDir;
+	Vec3 largestParams = max(intersectMaxPointPlanes, intersectMinPointPlanes);
+	F32 distToIntersect = min(min(largestParams.x, largestParams.y), largestParams.z);
+	return distToIntersect;
+}
+
 #endif

+ 33 - 0
shaders/LightFunctions.glsl

@@ -191,4 +191,37 @@ F32 computeAttenuationFactor(F32 lightRadius, Vec3 frag2Light)
 	return att * att;
 }
 
+// Given the probe properties trace a ray inside the probe and find the cube tex coordinates to sample
+Vec3 intersectProbe(Vec3 fragPos, // Ray origin
+	Vec3 rayDir, // Ray direction
+	Vec3 probeAabbMin,
+	Vec3 probeAabbMax,
+	Vec3 probeOrigin // Cubemap origin
+)
+{
+	// Compute the intersection point
+	F32 intresectionDist = rayAabbIntersectionInside(fragPos, rayDir, probeAabbMin, probeAabbMax);
+	Vec3 intersectionPoint = fragPos + intresectionDist * rayDir;
+
+	// Compute the cubemap vector
+	return intersectionPoint - probeOrigin;
+}
+
+// Compute a weight (factor) of fragPos against some probe's bounds. The weight will be zero when fragPos is close to
+// AABB bounds and 1.0 at fadeDistance and less.
+F32 computeProbeBlendWeight(Vec3 fragPos, // Doesn't need to be inside the AABB
+	Vec3 probeAabbMin,
+	Vec3 probeAabbMax,
+	F32 fadeDistance)
+{
+	// Compute the min distance of fragPos from the edges of the AABB
+	Vec3 distFromMin = fragPos - probeAabbMin;
+	Vec3 distFromMax = probeAabbMax - fragPos;
+	Vec3 minDistVec = min(distFromMin, distFromMax);
+	F32 minDist = min(minDistVec.x, min(minDistVec.y, minDistVec.z));
+
+	// Use saturate because minDist might be negative.
+	return saturate(minDist / fadeDistance);
+}
+
 #endif

+ 2 - 2
shaders/LightShading.ankiprog

@@ -117,7 +117,7 @@ void main()
 
 		LIGHTING_COMMON_BRDF();
 
-		if(light.m_diffuseColorTileSize.w >= 0.0)
+		ANKI_BRANCH if(light.m_diffuseColorTileSize.w >= 0.0)
 		{
 			F32 shadow = computeShadowFactorOmni(frag2Light, 
 				light.m_radiusPad1.x, 
@@ -143,7 +143,7 @@ void main()
 			l, light.m_outerCosInnerCos.x, light.m_outerCosInnerCos.y, light.m_lightDirRadius.xyz);
 
 		F32 shadowmapLayerIdx = light.m_diffuseColorShadowmapId.w;
-		if(shadowmapLayerIdx >= 0.0)
+		ANKI_BRANCH if(shadowmapLayerIdx >= 0.0)
 		{
 			F32 shadow = computeShadowFactorSpot(
 				light.m_texProjectionMat, worldPos, light.m_lightDirRadius.w, u_shadowTex);

+ 24 - 24
shaders/Reflections.ankiprog

@@ -174,36 +174,36 @@ void readReflectionsAndIrradianceFromProbes(
 
 	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++]];
-
-		F32 R2 = probe.m_positionRadiusSq.w;
-		Vec3 center = probe.m_positionRadiusSq.xyz;
-
-		// Get distance from the center of the probe
-		Vec3 f = worldPos - center;
-
-		// Cubemap UV in view space
-		Vec3 uv = computeCubemapVecAccurate(reflDir, R2, f);
-
-		// Read!
-		F32 cubemapIndex = probe.m_cubemapIndexPad3.x;
-		Vec3 c = textureLod(u_reflectionsTex, Vec4(uv, cubemapIndex), reflLod).rgb;
-
-		// Combine (lerp) with previous color
-		F32 d = dot(f, f);
-		F32 factor = d / R2;
-		factor = min(factor, 1.0);
-		specIndirect = mix(c, specIndirect, factor);
-
-		// Do the same for diffuse
-		uv = computeCubemapVecAccurate(normal, R2, f);
-		Vec3 id = textureLod(u_irradianceTex, Vec4(uv, cubemapIndex), 0.0).rgb;
-		diffIndirect = mix(id, diffIndirect, factor);
+		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()

+ 3 - 5
shaders/glsl_cpp_common/ClusteredShading.h

@@ -52,11 +52,9 @@ const U32 SIZEOF_SPOT_LIGHT = 4 * SIZEOF_VEC4 + SIZEOF_MAT4;
 // Representation of a reflection probe
 struct ReflectionProbe
 {
-	// Position of the prove in view space. Radius of probe squared
-	Vec4 m_positionRadiusSq;
-
-	// Slice in u_reflectionsTex vector.
-	Vec4 m_cubemapIndexPad3;
+	Vec4 m_positionCubemapIndex; // xyz: Position of the prove in view space. w: Slice in u_reflectionsTex vector.
+	Vec4 m_aabbMinPad1;
+	Vec4 m_aabbMaxPad1;
 };
 const U32 SIZEOF_REFLECTION_PROBE = 2 * SIZEOF_VEC4;
 

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

@@ -134,6 +134,7 @@ void ForwardShading::drawUpscale(const RenderingContext& ctx, RenderPassWorkCont
 	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,

+ 15 - 19
src/anki/renderer/LightBin.cpp

@@ -81,27 +81,22 @@ public:
 		m_index = i;
 	}
 
-	F32 getProbeRadius() const
+	void setProbeVolume(F32 v)
 	{
-		return F32(m_probeRadius) / F32(MAX_U16) * F32(MAX_PROBE_RADIUS);
-	}
-
-	void setProbeRadius(F32 r)
-	{
-		ANKI_ASSERT(r < MAX_PROBE_RADIUS);
-		m_probeRadius = r / F32(MAX_PROBE_RADIUS) * F32(MAX_U16);
+		ANKI_ASSERT(v < MAX_PROBE_VOLUME);
+		m_probeVolume = v / F32(MAX_PROBE_VOLUME) * F32(MAX_U16);
 	}
 
 	Bool operator<(const ClusterProbeIndex& b) const
 	{
-		ANKI_ASSERT(m_probeRadius > 0 && b.m_probeRadius > 0);
-		return (m_probeRadius != b.m_probeRadius) ? (m_probeRadius > b.m_probeRadius) : (m_index < b.m_index);
+		ANKI_ASSERT(m_probeVolume > 0 && b.m_probeVolume > 0);
+		return (m_probeVolume != b.m_probeVolume) ? (m_probeVolume > b.m_probeVolume) : (m_index < b.m_index);
 	}
 
 private:
-	static const U MAX_PROBE_RADIUS = 1000;
+	static const U MAX_PROBE_VOLUME = 1000;
 	U16 m_index;
-	U16 m_probeRadius;
+	U16 m_probeVolume;
 };
 
 /// WARNING: Keep it as small as possible. The number of clusters is huge
@@ -716,17 +711,16 @@ void LightBin::writeAndBinProbe(
 {
 	// Write it
 	ReflectionProbe probe;
-	probe.m_positionRadiusSq = Vec4(probeEl.m_worldPosition, probeEl.m_radius * probeEl.m_radius);
-	probe.m_cubemapIndexPad3 = Vec4(probeEl.m_textureArrayIndex, 0.0f, 0.0f, 0.0f);
+	probe.m_positionCubemapIndex = Vec4(probeEl.m_worldPosition, probeEl.m_textureArrayIndex);
+	probe.m_aabbMinPad1 = probeEl.m_aabbMin.xyz0();
+	probe.m_aabbMaxPad1 = probeEl.m_aabbMax.xyz0();
 
 	U idx = ctx.m_probeCount.fetchAdd(1);
 	ctx.m_probes[idx] = probe;
 
 	// Bin it
-	Sphere sphere(probeEl.m_worldPosition.xyz0(), probeEl.m_radius);
-	Aabb box;
-	sphere.computeAabb(box);
-	m_clusterer.bin(sphere, box, testResult);
+	Aabb box(probeEl.m_aabbMin, probeEl.m_aabbMax);
+	m_clusterer.bin(box, box, testResult);
 
 	auto it = testResult.getClustersBegin();
 	auto end = testResult.getClustersEnd();
@@ -742,7 +736,9 @@ void LightBin::writeAndBinProbe(
 
 		i = cluster.m_probeCount.fetchAdd(1) % MAX_PROBES_PER_CLUSTER;
 		cluster.m_probeIds[i].setIndex(idx);
-		cluster.m_probeIds[i].setProbeRadius(probeEl.m_radius);
+
+		Vec3 edges = probeEl.m_aabbMax - probeEl.m_aabbMin;
+		cluster.m_probeIds[i].setProbeVolume(edges.x() * edges.y() * edges.z());
 	}
 }
 

+ 2 - 1
src/anki/renderer/RenderQueue.h

@@ -120,7 +120,8 @@ public:
 	void* m_userData;
 	U64 m_uuid;
 	Vec3 m_worldPosition;
-	F32 m_radius;
+	Vec3 m_aabbMin;
+	Vec3 m_aabbMax;
 	Array<RenderQueue*, 6> m_renderQueues;
 	U32 m_textureArrayIndex; ///< Renderer internal.
 };

+ 22 - 15
src/anki/scene/ReflectionProbeNode.cpp

@@ -46,7 +46,7 @@ ReflectionProbeNode::~ReflectionProbeNode()
 {
 }
 
-Error ReflectionProbeNode::init(F32 radius)
+Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace)
 {
 	// Move component first
 	newComponent<MoveComponent>(this);
@@ -55,28 +55,28 @@ Error ReflectionProbeNode::init(F32 radius)
 	newComponent<ReflectionProbeMoveFeedbackComponent>(this);
 
 	// The frustum components
-	const F32 ang = toRad(90.0);
+	const F32 ang = toRad(90.0f);
 	const F32 zNear = FRUSTUM_NEAR_PLANE;
 
 	Mat3 rot;
 
-	rot = Mat3(Euler(0.0, -PI / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, PI));
+	rot = Mat3(Euler(0.0f, -PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	m_cubeSides[0].m_localTrf.setRotation(Mat3x4(rot));
-	rot = Mat3(Euler(0.0, PI / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, PI));
+	rot = Mat3(Euler(0.0f, PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	m_cubeSides[1].m_localTrf.setRotation(Mat3x4(rot));
-	rot = Mat3(Euler(PI / 2.0, 0.0, 0.0));
+	rot = Mat3(Euler(PI / 2.0f, 0.0f, 0.0f));
 	m_cubeSides[2].m_localTrf.setRotation(Mat3x4(rot));
-	rot = Mat3(Euler(-PI / 2.0, 0.0, 0.0));
+	rot = Mat3(Euler(-PI / 2.0f, 0.0f, 0.0f));
 	m_cubeSides[3].m_localTrf.setRotation(Mat3x4(rot));
-	rot = Mat3(Euler(0.0, PI, 0.0)) * Mat3(Euler(0.0, 0.0, PI));
+	rot = Mat3(Euler(0.0f, PI, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	m_cubeSides[4].m_localTrf.setRotation(Mat3x4(rot));
-	rot = Mat3(Euler(0.0, 0.0, PI));
+	rot = Mat3(Euler(0.0f, 0.0f, PI));
 	m_cubeSides[5].m_localTrf.setRotation(Mat3x4(rot));
 
 	for(U i = 0; i < 6; ++i)
 	{
-		m_cubeSides[i].m_localTrf.setOrigin(Vec4(0.0));
-		m_cubeSides[i].m_localTrf.setScale(1.0);
+		m_cubeSides[i].m_localTrf.setOrigin(Vec4(0.0f));
+		m_cubeSides[i].m_localTrf.setScale(1.0f);
 
 		m_cubeSides[i].m_frustum.setAll(ang, ang, zNear, EFFECTIVE_DISTANCE);
 		m_cubeSides[i].m_frustum.resetTransform(m_cubeSides[i].m_localTrf);
@@ -87,13 +87,16 @@ Error ReflectionProbeNode::init(F32 radius)
 	}
 
 	// Spatial component
-	m_spatialSphere.setCenter(Vec4(0.0));
-	m_spatialSphere.setRadius(radius);
-	newComponent<SpatialComponent>(this, &m_spatialSphere);
+	m_aabbMinLSpace = aabbMinLSpace.xyz();
+	m_aabbMaxLSpace = aabbMaxLSpace.xyz();
+	m_spatialAabb.setMin(aabbMinLSpace);
+	m_spatialAabb.setMax(aabbMaxLSpace);
+	newComponent<SpatialComponent>(this, &m_spatialAabb);
 
 	// Reflection probe comp
 	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>(this);
-	reflc->setRadius(radius);
+	reflc->setPosition(Vec4(0.0f));
+	reflc->setBoundingBox(aabbMinLSpace, aabbMaxLSpace);
 
 	return Error::NONE;
 }
@@ -120,11 +123,15 @@ void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
 	SpatialComponent& sp = getComponent<SpatialComponent>();
 	sp.markForUpdate();
 	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
-	m_spatialSphere.setCenter(move.getWorldTransform().getOrigin());
+	const Vec3 aabbMinWSpace = m_aabbMinLSpace + move.getWorldTransform().getOrigin().xyz();
+	const Vec3 aabbMaxWSpace = m_aabbMaxLSpace + move.getWorldTransform().getOrigin().xyz();
+	m_spatialAabb.setMin(aabbMinWSpace);
+	m_spatialAabb.setMax(aabbMaxWSpace);
 
 	// Update the refl comp
 	ReflectionProbeComponent& reflc = getComponent<ReflectionProbeComponent>();
 	reflc.setPosition(move.getWorldTransform().getOrigin());
+	reflc.setBoundingBox(aabbMinWSpace.xyz0(), aabbMaxWSpace.xyz0());
 }
 
 Error ReflectionProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime)

+ 4 - 15
src/anki/scene/ReflectionProbeNode.h

@@ -32,19 +32,7 @@ public:
 
 	~ReflectionProbeNode();
 
-	ANKI_USE_RESULT Error init(F32 radius);
-
-	U getCubemapArrayIndex() const
-	{
-		ANKI_ASSERT(m_cubemapArrayIdx < 0xFF);
-		return m_cubemapArrayIdx;
-	}
-
-	void setCubemapArrayIndex(U cubemapArrayIdx)
-	{
-		ANKI_ASSERT(cubemapArrayIdx < 0xFF);
-		m_cubemapArrayIdx = cubemapArrayIdx;
-	}
+	ANKI_USE_RESULT Error init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace);
 
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
@@ -57,8 +45,9 @@ private:
 	};
 
 	Array<CubeSide, 6> m_cubeSides;
-	Sphere m_spatialSphere;
-	U8 m_cubemapArrayIdx = 0xFF; ///< Used by the renderer
+	Vec3 m_aabbMinLSpace = Vec3(+1.0f);
+	Vec3 m_aabbMaxLSpace = Vec3(-1.0f);
+	Aabb m_spatialAabb;
 
 	void onMoveUpdate(MoveComponent& move);
 };

+ 20 - 30
src/anki/scene/components/ReflectionProbeComponent.h

@@ -25,67 +25,57 @@ public:
 	{
 	}
 
-	const Vec4& getPosition() const
+	Vec4 getPosition() const
 	{
-		return m_pos;
+		return m_pos.xyz0();
 	}
 
 	void setPosition(const Vec4& pos)
 	{
-		m_pos = pos.xyz0();
+		m_pos = pos.xyz();
 	}
 
-	F32 getRadius() const
+	void setBoundingBox(const Vec4& min, const Vec4& max)
 	{
-		ANKI_ASSERT(m_radius > 0.0);
-		return m_radius;
+		m_aabbMin = min.xyz();
+		m_aabbMax = max.xyz();
 	}
 
-	void setRadius(F32 radius)
+	Vec4 getBoundingBoxMin() const
 	{
-		ANKI_ASSERT(radius > 0.0);
-		m_radius = radius;
+		return m_aabbMin.xyz0();
 	}
 
-	Bool getMarkedForRendering() const
-	{
-		return m_markedForRendering;
-	}
-
-	void setMarkedForRendering(Bool render)
+	Vec4 getBoundingBoxMax() const
 	{
-		m_markedForRendering = render;
+		return m_aabbMax.xyz0();
 	}
 
-	void setTextureArrayIndex(U idx)
-	{
-		m_textureArrayIndex = idx;
-	}
-
-	U getTextureArrayIndex() const
+	Bool getMarkedForRendering() const
 	{
-		ANKI_ASSERT(m_textureArrayIndex < MAX_U16);
-		return m_textureArrayIndex;
+		return m_markedForRendering;
 	}
 
 	void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
 	{
+		ANKI_ASSERT(m_aabbMin < m_aabbMax);
+		ANKI_ASSERT(m_pos > m_aabbMin && m_pos < m_aabbMax);
 		el.m_feedbackCallback = reflectionProbeQueueElementFeedbackCallback;
 		el.m_userData = const_cast<ReflectionProbeComponent*>(this);
 		el.m_uuid = getUuid();
-		el.m_worldPosition = m_pos.xyz();
-		el.m_radius = m_radius;
+		el.m_worldPosition = m_pos;
+		el.m_aabbMin = m_aabbMin;
+		el.m_aabbMax = m_aabbMax;
 		el.m_textureArrayIndex = MAX_U32;
 		el.m_drawCallback = debugDrawCallback;
 	}
 
 private:
-	Vec4 m_pos = Vec4(0.0);
-	F32 m_radius = 0.0;
+	Vec3 m_pos = Vec3(0.0f);
+	Vec3 m_aabbMin = Vec3(+1.0f);
+	Vec3 m_aabbMax = Vec3(-1.0f);
 	Bool8 m_markedForRendering = false;
 
-	U16 m_textureArrayIndex = MAX_U16; ///< Used by the renderer
-
 	static void reflectionProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData)
 	{
 		ANKI_ASSERT(userData);

+ 14 - 4
src/anki/script/Scene.cpp

@@ -3107,7 +3107,7 @@ static inline int pwrapSceneGraphnewReflectionProbeNode(lua_State* l)
 	PtrSize size;
 	(void)size;
 
-	LuaBinder::checkArgsCount(l, 3);
+	LuaBinder::checkArgsCount(l, 4);
 
 	// Get "this" as "self"
 	if(LuaBinder::checkUserData(l, 1, classnameSceneGraph, -7754439619132389154, ud))
@@ -3124,14 +3124,24 @@ static inline int pwrapSceneGraphnewReflectionProbeNode(lua_State* l)
 		return -1;
 	}
 
-	F32 arg1;
-	if(LuaBinder::checkNumber(l, 3, arg1))
+	if(LuaBinder::checkUserData(l, 3, "Vec4", 6804478823655046386, ud))
+	{
+		return -1;
+	}
+
+	Vec4* iarg1 = ud->getData<Vec4>();
+	const Vec4& arg1(*iarg1);
+
+	if(LuaBinder::checkUserData(l, 4, "Vec4", 6804478823655046386, ud))
 	{
 		return -1;
 	}
 
+	Vec4* iarg2 = ud->getData<Vec4>();
+	const Vec4& arg2(*iarg2);
+
 	// Call the method
-	ReflectionProbeNode* ret = newSceneNode<ReflectionProbeNode>(self, arg0, arg1);
+	ReflectionProbeNode* ret = newSceneNode<ReflectionProbeNode>(self, arg0, arg1, arg2);
 
 	// Push return value
 	if(ANKI_UNLIKELY(ret == nullptr))

+ 3 - 2
src/anki/script/Scene.xml

@@ -369,10 +369,11 @@ static SceneGraph* getSceneGraph(lua_State* l)
 					<return>ParticleEmitterNode*</return>
 				</method>
 				<method name="newReflectionProbeNode">
-					<overrideCall><![CDATA[ReflectionProbeNode* ret = newSceneNode<ReflectionProbeNode>(self, arg0, arg1);]]></overrideCall>
+					<overrideCall><![CDATA[ReflectionProbeNode* ret = newSceneNode<ReflectionProbeNode>(self, arg0, arg1, arg2);]]></overrideCall>
 					<args>
 						<arg>const CString&amp;</arg>
-						<arg>F32</arg>
+						<arg>const Vec4&amp;</arg>
+						<arg>const Vec4&amp;</arg>
 					</args>
 					<return>ReflectionProbeNode*</return>
 				</method>

+ 9 - 4
tools/scene/Exporter.cpp

@@ -819,9 +819,12 @@ void Exporter::visitNode(const aiNode* ainode)
 				aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
 				probe.m_position = aiVector3D(trf.a4, trf.b4, trf.c4);
 
-				aiVector3D zAxis(trf.a3, trf.b3, trf.c3);
-				float scale = zAxis.Length();
-				probe.m_radius = scale;
+				aiVector3D scale(trf.a1, trf.b2, trf.c3);
+				assert(scale.x > 0.0f && scale.y > 0.0f && scale.z > 0.0f);
+
+				aiVector3D half = scale;
+				probe.m_aabbMin = probe.m_position - half - probe.m_position;
+				probe.m_aabbMax = probe.m_position + half - probe.m_position;
 
 				m_reflectionProbes.push_back(probe);
 
@@ -1049,7 +1052,9 @@ void Exporter::exportAll()
 	for(const ReflectionProbe& probe : m_reflectionProbes)
 	{
 		std::string name = "reflprobe" + std::to_string(i);
-		file << "\nnode = scene:newReflectionProbeNode(\"" << name << "\", " << probe.m_radius << ")\n";
+		file << "\nnode = scene:newReflectionProbeNode(\"" << name << "\", Vec4.new(" << probe.m_aabbMin.x << ", "
+			 << probe.m_aabbMin.y << ", " << probe.m_aabbMin.z << ", 0), Vec4.new(" << probe.m_aabbMax.x << ", "
+			 << probe.m_aabbMax.y << ", " << probe.m_aabbMax.z << ", 0))\n";
 
 		aiMatrix4x4 trf;
 		aiMatrix4x4::Translation(probe.m_position, trf);

+ 2 - 1
tools/scene/Exporter.h

@@ -74,7 +74,8 @@ class ReflectionProbe
 {
 public:
 	aiVector3D m_position;
-	float m_radius;
+	aiVector3D m_aabbMin;
+	aiVector3D m_aabbMax;
 };
 
 class ReflectionProxy