Browse Source

CSM kind of works now

Panagiotis Christopoulos Charitos 7 năm trước cách đây
mục cha
commit
0bb20cb90e

+ 16 - 0
shaders/LightFunctions.glsl

@@ -158,6 +158,22 @@ F32 computeShadowFactorOmni(
 	return saturate(exp(ESM_CONSTANT * (shadowFactor - linearDepth)));
 }
 
+// Compute the shadow factor of a directional light
+F32 computeShadowFactorDirLight(DirectionalLight light, U32 cascadeIdx, Vec3 worldPos, sampler2D shadowMap)
+{
+	Mat4 lightProjectionMat = light.m_textureMatrices[cascadeIdx];
+
+	Vec4 texCoords4 = lightProjectionMat * Vec4(worldPos, 1.0);
+	Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+
+	F32 cascadeLinearDepth = texCoords3.z;
+
+	F32 shadowFactor = textureLod(shadowMap, texCoords3.xy, 0.0).r;
+	shadowFactor = saturate(exp(ESM_CONSTANT * 50.0 * (shadowFactor - cascadeLinearDepth)));
+
+	return shadowFactor;
+}
+
 // 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 F32 R2, in Vec3 f)

+ 9 - 13
shaders/LightShading.glslp

@@ -146,30 +146,26 @@ void main()
 	out_color = gbuffer.m_diffuse * gbuffer.m_emission;
 
 	// Dir light
-	if(u_dirLight.m_cascadeCount > 0u && false)
+	Vec3 viewDir = normalize(u_cameraPos - worldPos);
+	if(u_dirLight.m_cascadeCount > 0u)
 	{
 		F32 linearDepth = linearizeDepth(depth, u_near, u_far);
-
 		F32 cascadeCountf = F32(u_dirLight.m_cascadeCount);
+		U32 cascadeIdx = min(U32(linearDepth * cascadeCountf), u_dirLight.m_cascadeCount - 1u);
 
-		U32 cascadeIdx = U32(linearDepth * cascadeCountf);
-		cascadeIdx = min(cascadeIdx, u_dirLight.m_cascadeCount);
+		F32 shadowFactor = computeShadowFactorDirLight(u_dirLight, cascadeIdx, worldPos, u_shadowTex);
 
-		Mat4 lightProjectionMat = u_dirLight.m_textureMatrices[cascadeIdx];
+		Vec3 l = u_dirLight.m_dir;
 
-		Vec4 texCoords4 = lightProjectionMat * Vec4(worldPos, 1.0);
-		Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+		F32 lambert = max(gbuffer.m_subsurface, dot(l, gbuffer.m_normal));
 
-		F32 cascadeLinearDepth = texCoords3.z;
+		Vec3 diffC = diffuseLambert(gbuffer.m_diffuse);
+		Vec3 specC = computeSpecularColorBrdf(gbuffer, viewDir, l);
 
-		F32 shadowFactor = textureLod(u_shadowTex, texCoords3.xy, 0.0).r;
-		shadowFactor = saturate(exp(ESM_CONSTANT * (shadowFactor - cascadeLinearDepth)));
-
-		out_color += diffuseLambert(gbuffer.m_diffuse) * u_dirLight.m_diffuseColor * shadowFactor;
+		out_color += (diffC + specC) * u_dirLight.m_diffuseColor * (shadowFactor * lambert);
 	}
 
 	// Point lights
-	Vec3 viewDir = normalize(u_cameraPos - worldPos);
 	U32 idx;
 	ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
 	{

+ 25 - 10
src/anki/renderer/ShadowMapping.cpp

@@ -375,7 +375,7 @@ TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 	const U64* faceTimestamps,
 	const U32* faceIndices,
 	const U32* drawcallsCount,
-	U32 lod,
+	const U32* lods,
 	Viewport* esmTileViewports,
 	Viewport* scratchTileViewports,
 	TileAllocatorResult* subResults)
@@ -385,7 +385,7 @@ TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 	ANKI_ASSERT(faceTimestamps);
 	ANKI_ASSERT(faceIndices);
 	ANKI_ASSERT(drawcallsCount);
-	ANKI_ASSERT(lod < m_lodCount);
+	ANKI_ASSERT(lods);
 
 	TileAllocatorResult res;
 
@@ -397,7 +397,7 @@ TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 			lightUuid,
 			faceIndices[i],
 			drawcallsCount[i],
-			lod,
+			lods[i],
 			esmTileViewports[i]);
 
 		if(res == TileAllocatorResult::ALLOCATION_FAILED)
@@ -438,7 +438,7 @@ TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
 			lightUuid,
 			faceIndices[i],
 			drawcallsCount[i],
-			lod,
+			lods[i],
 			scratchTileViewports[i]);
 
 		if(res == TileAllocatorResult::ALLOCATION_FAILED)
@@ -515,6 +515,7 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 		Array<Viewport, MAX_SHADOW_CASCADES> esmViewports;
 		Array<Viewport, MAX_SHADOW_CASCADES> scratchViewports;
 		Array<TileAllocatorResult, MAX_SHADOW_CASCADES> subResults;
+		Array<U32, MAX_SHADOW_CASCADES> lods;
 
 		for(U cascade = 0; cascade < light.m_shadowCascadeCount; ++cascade)
 		{
@@ -522,6 +523,15 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 			timestamps[cascade] = m_r->getGlobalTimestamp(); // This light is always updated
 			cascadeIndices[cascade] = cascade;
 			drawcallCounts[cascade] = 1; // Doesn't matter
+
+			if(cascade <= 1)
+			{
+				lods[cascade] = m_lodCount - 1; // Always the best quality
+			}
+			else
+			{
+				lods[cascade] = lods[0] - 1;
+			}
 		}
 
 		const Bool allocationFailed = allocateTilesAndScratchTiles(light.m_uuid,
@@ -529,7 +539,7 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 										  &timestamps[0],
 										  &cascadeIndices[0],
 										  &drawcallCounts[0],
-										  m_lodCount - 1, // Always the best quality
+										  &lods[0],
 										  &esmViewports[0],
 										  &scratchViewports[0],
 										  &subResults[0])
@@ -572,7 +582,12 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 		Array<Viewport, 6> esmViewports;
 		Array<Viewport, 6> scratchViewports;
 		Array<TileAllocatorResult, 6> subResults;
+		Array<U32, 6> lods;
 		U numOfFacesThatHaveDrawcalls = 0;
+
+		Bool blurEsm;
+		const U lod = choseLod(cameraOrigin, *light, blurEsm);
+
 		for(U face = 0; face < 6; ++face)
 		{
 			ANKI_ASSERT(light->m_shadowRenderQueues[face]);
@@ -587,19 +602,19 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 				drawcallCounts[numOfFacesThatHaveDrawcalls] =
 					light->m_shadowRenderQueues[face]->m_renderables.getSize();
 
+				lods[numOfFacesThatHaveDrawcalls] = lod;
+
 				++numOfFacesThatHaveDrawcalls;
 			}
 		}
 
-		Bool blurEsm;
-		const U lod = choseLod(cameraOrigin, *light, blurEsm);
 		const Bool allocationFailed = numOfFacesThatHaveDrawcalls == 0
 									  || allocateTilesAndScratchTiles(light->m_uuid,
 											 numOfFacesThatHaveDrawcalls,
 											 &timestamps[0],
 											 &faceIndices[0],
 											 &drawcallCounts[0],
-											 lod,
+											 &lods[0],
 											 &esmViewports[0],
 											 &scratchViewports[0],
 											 &subResults[0])
@@ -676,14 +691,14 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 		const U32 localDrawcallCount = light->m_shadowRenderQueue->m_renderables.getSize();
 
 		Bool blurEsm;
-		const U lod = choseLod(cameraOrigin, *light, blurEsm);
+		const U32 lod = choseLod(cameraOrigin, *light, blurEsm);
 		const Bool allocationFailed = localDrawcallCount == 0
 									  || allocateTilesAndScratchTiles(light->m_uuid,
 											 1,
 											 &light->m_shadowRenderQueue->m_shadowRenderablesLastUpdateTimestamp,
 											 &faceIdx,
 											 &localDrawcallCount,
-											 lod,
+											 &lod,
 											 &esmViewport,
 											 &scratchViewport,
 											 &subResult)

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

@@ -108,7 +108,7 @@ private:
 		const U64* faceTimestamps,
 		const U32* faceIndices,
 		const U32* drawcallsCount,
-		U32 lod,
+		const U32* lods,
 		Viewport* esmTileViewports,
 		Viewport* scratchTileViewports,
 		TileAllocatorResult* subResults);

+ 2 - 2
src/anki/scene/LightNode.cpp

@@ -336,8 +336,8 @@ Error DirectionalLightNode::init()
 	newComponent<SpatialComponent>(this, &m_boundingBox);
 
 	// Make the bounding box large enough so it will always be visible
-	m_boundingBox.setMin(Vec3(-5000.f));
-	m_boundingBox.setMax(Vec3(+5000.f));
+	m_boundingBox.setMin(getSceneGraph().getSceneMin());
+	m_boundingBox.setMax(getSceneGraph().getSceneMax());
 
 	return Error::NONE;
 }

+ 4 - 0
src/anki/scene/Octree.cpp

@@ -93,6 +93,10 @@ void Octree::place(const Aabb& volume, OctreePlaceable* placeable)
 	// And re-place it
 	placeRecursive(volume, placeable, m_rootLeaf, 0);
 	++m_placeableCount;
+
+	// Update the actual scene bounds
+	m_actualSceneAabbMin = m_actualSceneAabbMin.min(volume.getMin().xyz());
+	m_actualSceneAabbMax = m_actualSceneAabbMax.max(volume.getMax().xyz());
 }
 
 void Octree::remove(OctreePlaceable& placeable)

+ 18 - 0
src/anki/scene/Octree.h

@@ -106,6 +106,20 @@ public:
 		debugDrawRecursive(*m_rootLeaf, drawer);
 	}
 
+	/// Get the min bound of the scene as calculated by the objects that were placed inside the Octree.
+	const Vec3& getActualSceneMinBound() const
+	{
+		ANKI_ASSERT(m_actualSceneAabbMin.x() < MAX_F32);
+		return m_actualSceneAabbMin;
+	}
+
+	/// Get the max bound of the scene as calculated by the objects that were placed inside the Octree.
+	const Vec3& getActualSceneMaxBound() const
+	{
+		ANKI_ASSERT(m_actualSceneAabbMax.x() > MIN_F32);
+		return m_actualSceneAabbMax;
+	}
+
 private:
 	class GatherParallelCtx;
 	class GatherParallelTaskCtx;
@@ -201,6 +215,10 @@ private:
 	Leaf* m_rootLeaf = nullptr;
 	U32 m_placeableCount = 0;
 
+	/// Compute the min of the scene bounds based on what is placed inside the octree.
+	Vec3 m_actualSceneAabbMin = Vec3(MAX_F32);
+	Vec3 m_actualSceneAabbMax = Vec3(MIN_F32);
+
 	Leaf* newLeaf()
 	{
 		return m_leafAlloc.newInstance(m_alloc);

+ 43 - 9
src/anki/scene/components/LightComponent.cpp

@@ -4,6 +4,9 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/scene/components/LightComponent.h>
+#include <anki/scene/SceneNode.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/Octree.h>
 #include <anki/collision/Frustum.h>
 #include <shaders/glsl_cpp_common/ClusteredShading.h>
 
@@ -60,6 +63,35 @@ Error LightComponent::update(SceneNode& node, Second prevTime, Second crntTime,
 
 	m_flags.unset(DIRTY | TRF_DIRTY);
 
+	// Update the scene bounds always
+	if(m_type == LightComponentType::DIRECTIONAL)
+	{
+		// Get some values fro the octree
+		const Vec3& sceneWSpaceMin = node.getSceneGraph().getOctree().getActualSceneMinBound();
+		const Vec3& sceneWSpaceMax = node.getSceneGraph().getOctree().getActualSceneMaxBound();
+
+		// Gather the 8 scene points
+		Array<Vec4, 8> sceneEdgesWSpace;
+		sceneEdgesWSpace[0] = Vec4(sceneWSpaceMin.x(), sceneWSpaceMin.y(), sceneWSpaceMin.z(), 1.0f);
+		sceneEdgesWSpace[1] = Vec4(sceneWSpaceMin.x(), sceneWSpaceMin.y(), sceneWSpaceMax.z(), 1.0f);
+		sceneEdgesWSpace[2] = Vec4(sceneWSpaceMin.x(), sceneWSpaceMax.y(), sceneWSpaceMin.z(), 1.0f);
+		sceneEdgesWSpace[3] = Vec4(sceneWSpaceMin.x(), sceneWSpaceMax.y(), sceneWSpaceMax.z(), 1.0f);
+		sceneEdgesWSpace[4] = Vec4(sceneWSpaceMax.x(), sceneWSpaceMin.y(), sceneWSpaceMin.z(), 1.0f);
+		sceneEdgesWSpace[5] = Vec4(sceneWSpaceMax.x(), sceneWSpaceMin.y(), sceneWSpaceMax.z(), 1.0f);
+		sceneEdgesWSpace[6] = Vec4(sceneWSpaceMax.x(), sceneWSpaceMax.y(), sceneWSpaceMin.z(), 1.0f);
+		sceneEdgesWSpace[7] = Vec4(sceneWSpaceMax.x(), sceneWSpaceMax.y(), sceneWSpaceMax.z(), 1.0f);
+
+		// Transform the 8 scene points to light space
+		m_dir.m_sceneAabbMaxZLightSpace = MIN_F32;
+		const Mat4 viewMat = Mat4(m_trf).getInverse();
+		for(const Vec4& point : sceneEdgesWSpace)
+		{
+			const Vec4 lightSpaceEdge = viewMat * point;
+
+			m_dir.m_sceneAabbMaxZLightSpace = max(m_dir.m_sceneAabbMaxZLightSpace, lightSpaceEdge.z());
+		}
+	}
+
 	return Error::NONE;
 }
 
@@ -93,12 +125,12 @@ void LightComponent::setupDirectionalLightQueueElement(
 			Vec4(0.0f, 0.0f, 0.0f, 1.0f); // Eye
 		for(U i = 0; i < m_dir.m_cascadeCount; ++i)
 		{
-			const F32 clusterFarNearDist = far / F32(m_dir.m_cascadeCount);
-			const F32 clusterFar = F32(i + 1) * clusterFarNearDist;
+			const F32 cascadeFarNearDist = far / F32(m_dir.m_cascadeCount);
+			const F32 cascadeFar = F32(i + 1) * cascadeFarNearDist;
 
-			const F32 x = clusterFar * tan(fovY / 2.0f) * fovX / fovY;
-			const F32 y = tan(fovY / 2.0f) * clusterFar;
-			const F32 z = -clusterFar;
+			const F32 x = cascadeFar * tan(fovY / 2.0f) * fovX / fovY;
+			const F32 y = tan(fovY / 2.0f) * cascadeFar;
+			const F32 z = -cascadeFar;
 
 			U idx = (i + 1) * 4;
 			edgesLocalSpace[idx++] = Vec4(x, y, z, 1.0f); // top right
@@ -128,8 +160,10 @@ void LightComponent::setupDirectionalLightQueueElement(
 				aabbMax = aabbMax.max(edgesLightSpace[j].xyz());
 			}
 
-			ANKI_ASSERT(aabbMax > aabbMin);
+			// Adjust the max to take into account the scene bounds
+			aabbMax.z() = max(aabbMax.z(), m_dir.m_sceneAabbMaxZLightSpace);
 
+			ANKI_ASSERT(aabbMax > aabbMin);
 			minMaxes[i][0] = aabbMin;
 			minMaxes[i][1] = aabbMax;
 		}
@@ -148,12 +182,12 @@ void LightComponent::setupDirectionalLightQueueElement(
 				halfDistances.y(),
 				-halfDistances.y(),
 				LIGHT_FRUSTUM_NEAR_PLANE,
-				max.z() - min.z());
+				max.z() - min.z() - LIGHT_FRUSTUM_NEAR_PLANE);
 
 			Vec4 eye;
 			eye.x() = (max.x() + min.x()) / 2.0f;
 			eye.y() = (max.y() + min.y()) / 2.0f;
-			eye.z() = 0.0f;
+			eye.z() = max.z();
 			eye.w() = 1.0f;
 			eye = lightTrf * eye;
 
@@ -170,7 +204,7 @@ void LightComponent::setupDirectionalLightQueueElement(
 			cascadeFrustum.setAll(-halfDistances.x(),
 				halfDistances.x(),
 				LIGHT_FRUSTUM_NEAR_PLANE,
-				max.z() - min.z(),
+				max.z() - min.z() - LIGHT_FRUSTUM_NEAR_PLANE,
 				halfDistances.y(),
 				-halfDistances.y());
 			cascadeFrustum.transform(cascadeTransform);

+ 1 - 0
src/anki/scene/components/LightComponent.h

@@ -185,6 +185,7 @@ private:
 	struct Dir
 	{
 		U32 m_cascadeCount;
+		F32 m_sceneAabbMaxZLightSpace;
 	};
 
 	union