Jelajahi Sumber

Add directional light support to the shadowmapping pass

Panagiotis Christopoulos Charitos 7 tahun lalu
induk
melakukan
502bee6c79

+ 0 - 54
src/anki/physics/Common.h

@@ -109,60 +109,6 @@ ANKI_USE_RESULT inline Transform toAnki(const btTransform& t)
 	out.setScale(1.0f);
 	return out;
 }
-
-/// A wrapper template to compensate for of the fact that Bullet classes get initialized in the constructor.
-template<typename TBtClass>
-class BtClassWrapper
-{
-public:
-	BtClassWrapper()
-	{
-	}
-
-	template<typename... TArgs>
-	void init(TArgs&&... args)
-	{
-		::new(&m_data[0]) TBtClass(std::forward<TArgs>(args)...);
-	}
-
-	void destroy()
-	{
-		reinterpret_cast<TBtClass*>(&m_data[0])->~TBtClass();
-	}
-
-	TBtClass* operator->()
-	{
-		return reinterpret_cast<TBtClass*>(&m_data[0]);
-	}
-
-	const TBtClass* operator->() const
-	{
-		return reinterpret_cast<const TBtClass*>(&m_data[0]);
-	}
-
-	TBtClass& operator*()
-	{
-		return *reinterpret_cast<TBtClass*>(&m_data[0]);
-	}
-
-	const TBtClass& operator*() const
-	{
-		return *reinterpret_cast<const TBtClass*>(&m_data[0]);
-	}
-
-	TBtClass* get()
-	{
-		return reinterpret_cast<TBtClass*>(&m_data[0]);
-	}
-
-	const TBtClass* get() const
-	{
-		return reinterpret_cast<const TBtClass*>(&m_data[0]);
-	}
-
-private:
-	alignas(alignof(TBtClass)) Array<U8, sizeof(TBtClass)> m_data;
-};
 /// @}
 
 } // end namespace anki

+ 2 - 1
src/anki/physics/PhysicsBody.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/physics/PhysicsObject.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -117,7 +118,7 @@ private:
 	};
 
 	/// Store the data of the btRigidBody in place to avoid additional allocations.
-	BtClassWrapper<btRigidBody> m_body;
+	ClassWrapper<btRigidBody> m_body;
 
 	Transform m_trf = Transform::getIdentity();
 	MotionState m_motionState;

+ 7 - 6
src/anki/physics/PhysicsCollisionShape.h

@@ -7,6 +7,7 @@
 
 #include <anki/physics/PhysicsObject.h>
 #include <anki/util/WeakArray.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -43,16 +44,16 @@ protected:
 	class TriMesh
 	{
 	public:
-		BtClassWrapper<btGImpactMeshShape> m_dynamic;
-		BtClassWrapper<btBvhTriangleMeshShape> m_static;
+		ClassWrapper<btGImpactMeshShape> m_dynamic;
+		ClassWrapper<btBvhTriangleMeshShape> m_static;
 	};
 
 	// All shapes
 	union
 	{
-		BtClassWrapper<btBoxShape> m_box;
-		BtClassWrapper<btSphereShape> m_sphere;
-		BtClassWrapper<btConvexHullShape> m_convex;
+		ClassWrapper<btBoxShape> m_box;
+		ClassWrapper<btSphereShape> m_sphere;
+		ClassWrapper<btConvexHullShape> m_convex;
 		TriMesh m_triMesh;
 	};
 
@@ -129,7 +130,7 @@ class PhysicsTriangleSoup final : public PhysicsCollisionShape
 	ANKI_PHYSICS_OBJECT
 
 private:
-	BtClassWrapper<btTriangleMesh> m_mesh;
+	ClassWrapper<btTriangleMesh> m_mesh;
 
 	PhysicsTriangleSoup(
 		PhysicsWorld* world, ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices, Bool convex = false);

+ 3 - 2
src/anki/physics/PhysicsJoint.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/physics/PhysicsObject.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -40,8 +41,8 @@ public:
 protected:
 	union
 	{
-		BtClassWrapper<btPoint2PointConstraint> m_p2p;
-		BtClassWrapper<btHingeConstraint> m_hinge;
+		ClassWrapper<btPoint2PointConstraint> m_p2p;
+		ClassWrapper<btHingeConstraint> m_hinge;
 	};
 
 	PhysicsBodyPtr m_bodyA;

+ 4 - 3
src/anki/physics/PhysicsPlayerController.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/physics/PhysicsObject.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -49,9 +50,9 @@ public:
 	}
 
 private:
-	BtClassWrapper<btPairCachingGhostObject> m_ghostObject;
-	BtClassWrapper<btCapsuleShape> m_convexShape;
-	BtClassWrapper<btKinematicCharacterController> m_controller;
+	ClassWrapper<btPairCachingGhostObject> m_ghostObject;
+	ClassWrapper<btCapsuleShape> m_convexShape;
+	ClassWrapper<btKinematicCharacterController> m_controller;
 
 	Transform m_prevTrf = Transform::getIdentity();
 

+ 2 - 1
src/anki/physics/PhysicsTrigger.h

@@ -7,6 +7,7 @@
 
 #include <anki/physics/PhysicsObject.h>
 #include <anki/util/WeakArray.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -46,7 +47,7 @@ public:
 
 private:
 	PhysicsCollisionShapePtr m_shape;
-	BtClassWrapper<btGhostObject> m_ghostShape;
+	ClassWrapper<btGhostObject> m_ghostShape;
 
 	PhysicsTriggerProcessContactCallback* m_contactCallback = nullptr;
 

+ 7 - 6
src/anki/physics/PhysicsWorld.h

@@ -9,6 +9,7 @@
 #include <anki/physics/PhysicsObject.h>
 #include <anki/util/List.h>
 #include <anki/util/WeakArray.h>
+#include <anki/util/ClassWrapper.h>
 
 namespace anki
 {
@@ -110,14 +111,14 @@ private:
 	HeapAllocator<U8> m_alloc;
 	StackAllocator<U8> m_tmpAlloc;
 
-	BtClassWrapper<btDbvtBroadphase> m_broadphase;
-	BtClassWrapper<btGhostPairCallback> m_gpc;
+	ClassWrapper<btDbvtBroadphase> m_broadphase;
+	ClassWrapper<btGhostPairCallback> m_gpc;
 	MyOverlapFilterCallback* m_filterCallback = nullptr;
 
-	BtClassWrapper<btDefaultCollisionConfiguration> m_collisionConfig;
-	BtClassWrapper<btCollisionDispatcher> m_dispatcher;
-	BtClassWrapper<btSequentialImpulseConstraintSolver> m_solver;
-	BtClassWrapper<btDiscreteDynamicsWorld> m_world;
+	ClassWrapper<btDefaultCollisionConfiguration> m_collisionConfig;
+	ClassWrapper<btCollisionDispatcher> m_dispatcher;
+	ClassWrapper<btSequentialImpulseConstraintSolver> m_solver;
+	ClassWrapper<btDiscreteDynamicsWorld> m_world;
 	mutable Mutex m_btWorldMtx;
 
 	Array<IntrusiveList<PhysicsObject>, U(PhysicsObjectType::COUNT)> m_objectLists;

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

@@ -129,6 +129,7 @@ public:
 	Array<RenderQueue*, MAX_SHADOW_CASCADES> m_shadowRenderQueues;
 	const void* m_userData;
 	RenderQueueDrawCallback m_drawCallback;
+	U64 m_uuid;
 	Vec3 m_diffuseColor;
 	Vec3 m_direction;
 	U8 m_shadowCascadeCount;

+ 77 - 5
src/anki/renderer/ShadowMapping.cpp

@@ -299,16 +299,32 @@ U ShadowMapping::choseLod(const Vec4& cameraOrigin, const PointLightQueueElement
 
 U ShadowMapping::choseLod(const Vec4& cameraOrigin, const SpotLightQueueElement& light) const
 {
-	const Vec4 lightPos = light.m_worldTransform.getTranslationPart().xyz0();
-	const F32 distFromTheCamera = (cameraOrigin - lightPos).getLength() - light.m_distance;
+	// Get some data
+	const Vec4 coneOrigin = light.m_worldTransform.getTranslationPart().xyz0();
+	const Vec4 coneDir = -light.m_worldTransform.getZAxis().xyz0();
+	const F32 coneAngle = light.m_outerAngle;
+
+	// Compute the distance from the camera to the light cone
+	const Vec4 V = cameraOrigin - coneOrigin;
+	const F32 VlenSq = V.dot(V);
+	const F32 V1len = V.dot(coneDir);
+	const F32 distFromTheCamera = cos(coneAngle) * sqrt(VlenSq - V1len * V1len) - V1len * sin(coneAngle);
+
+	U lod;
 	if(distFromTheCamera < m_lodDistances[0])
 	{
-		return 1;
+		lod = 2;
+	}
+	else if(distFromTheCamera < m_lodDistances[1])
+	{
+		lod = 1;
 	}
 	else
 	{
-		return 0;
+		lod = 0;
 	}
+
+	return lod;
 }
 
 TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
@@ -431,7 +447,63 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 #endif
 	}
 
-	// Process the point lights first.
+	// Process the directional light first.
+	if(ctx.m_renderQueue->m_directionalLight.m_shadowCascadeCount > 0)
+	{
+		DirectionalLightQueueElement& light = ctx.m_renderQueue->m_directionalLight;
+
+		Array<U64, MAX_SHADOW_CASCADES> timestamps;
+		Array<U32, MAX_SHADOW_CASCADES> cascadeIndices;
+		Array<U32, MAX_SHADOW_CASCADES> drawcallCounts;
+		Array<Viewport, MAX_SHADOW_CASCADES> esmViewports;
+		Array<Viewport, MAX_SHADOW_CASCADES> scratchViewports;
+		Array<TileAllocatorResult, MAX_SHADOW_CASCADES> subResults;
+
+		for(U cascade = 0; cascade < light.m_shadowCascadeCount; ++cascade)
+		{
+			ANKI_ASSERT(light.m_shadowRenderQueues[cascade]);
+			timestamps[cascade] = m_r->getGlobalTimestamp(); // This light is always updated
+			cascadeIndices[cascade] = cascade;
+			drawcallCounts[cascade] = 1; // Doesn't matter
+		}
+
+		const Bool allocationFailed = allocateTilesAndScratchTiles(light.m_uuid,
+										  light.m_shadowCascadeCount,
+										  &timestamps[0],
+										  &cascadeIndices[0],
+										  &drawcallCounts[0],
+										  m_lodCount - 1, // Always the best quality
+										  &esmViewports[0],
+										  &scratchViewports[0],
+										  &subResults[0])
+									  == TileAllocatorResult::ALLOCATION_FAILED;
+
+		if(!allocationFailed)
+		{
+			for(U cascade = 0; cascade < light.m_shadowCascadeCount; ++cascade)
+			{
+				// Update the texture matrix to point to the correct region in the atlas
+				light.m_textureMatrices[cascade] =
+					createSpotLightTextureMatrix(esmViewports[cascade]) * light.m_textureMatrices[cascade];
+
+				// Push work
+				newScratchAndEsmResloveRenderWorkItems(esmViewports[cascade],
+					scratchViewports[cascade],
+					light.m_shadowRenderQueues[cascade],
+					lightsToRender,
+					esmWorkItems,
+					drawcallCount);
+			}
+		}
+		else
+		{
+			// Light can't be a caster this frame
+			light.m_shadowCascadeCount = 0;
+			zeroMemory(light.m_shadowRenderQueues);
+		}
+	}
+
+	// Process the point lights.
 	for(PointLightQueueElement* light : ctx.m_renderQueue->m_shadowPointLights)
 	{
 		// Prepare data to allocate tiles and allocate

+ 0 - 1
src/anki/scene/SceneGraph.cpp

@@ -64,7 +64,6 @@ Error SceneGraph::init(AllocAlignedCallback allocCb,
 	m_globalTimestamp = globalTimestamp;
 	m_threadHive = threadHive;
 	m_resources = resources;
-	m_objectsMarkedForDeletionCount.store(0);
 	m_gr = &m_resources->getGrManager();
 	m_physics = &m_resources->getPhysicsWorld();
 	m_input = input;

+ 3 - 3
src/anki/scene/SceneGraph.h

@@ -212,7 +212,7 @@ anki_internal:
 
 	U64 getNewUuid()
 	{
-		return m_nodesUuid++;
+		return m_nodesUuid.fetchAdd(1);
 	}
 
 	F32 getEarlyZDistance() const
@@ -258,11 +258,11 @@ private:
 	Vec3 m_sceneMin = {-1000.0f, -200.0f, -1000.0f};
 	Vec3 m_sceneMax = {1000.0f, 200.0f, 1000.0f};
 
-	Atomic<U32> m_objectsMarkedForDeletionCount;
+	Atomic<U32> m_objectsMarkedForDeletionCount = {0};
 
 	F32 m_maxReflectionProxyDistance = 0.0;
 
-	U64 m_nodesUuid = 0;
+	Atomic<U64> m_nodesUuid = {1};
 
 	F32 m_earlyZDist = -1.0;
 

+ 5 - 0
src/anki/scene/Visibility.cpp

@@ -431,6 +431,11 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 					result.m_directionalLight.m_shadowRenderQueues[i] = &nextQueues[i];
 				}
 
+				// Despite the fact that it's the same light it will have different properties if viewed by different
+				// cameras. If the renderer finds the same UUID it will think it's cached and use wrong shadow tiles.
+				// That's why we need to change its UUID and bind it to the frustum that is currently viewing the light
+				result.m_directionalLight.m_uuid = testedNode.getUuid();
+
 				// Create some dummy frustum components and manually update them
 				FrustumComponent* cascadeFrustumComponents = reinterpret_cast<FrustumComponent*>(
 					alloc.allocate(cascadeCount * sizeof(FrustumComponent), alignof(FrustumComponent)));

+ 82 - 0
src/anki/util/ClassWrapper.h

@@ -0,0 +1,82 @@
+// 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 <anki/util/Array.h>
+
+namespace anki
+{
+
+/// @addtogroup util_misc
+/// @{
+
+/// A wrapper template to compensate for the fact that some classes get initialized in the constructor but that's not
+/// always desirable. One solution is to use a pointer and dynamic allocation but that creates an indirection and it
+/// might cause bad cache locality. With this wrapper is scenario is avoided.
+template<typename TClass>
+class ClassWrapper
+{
+public:
+	ClassWrapper()
+	{
+	}
+
+	/// Call the constructor of the TClass.
+	template<typename... TArgs>
+	void init(TArgs&&... args)
+	{
+		::new(&m_data[0]) TClass(std::forward<TArgs>(args)...);
+	}
+
+	/// Call the destructor of the TClass.
+	void destroy()
+	{
+		reinterpret_cast<TClass*>(&m_data[0])->~TClass();
+	}
+
+	/// Access the instance.
+	TClass* operator->()
+	{
+		return reinterpret_cast<TClass*>(&m_data[0]);
+	}
+
+	/// Access the instance.
+	const TClass* operator->() const
+	{
+		return reinterpret_cast<const TClass*>(&m_data[0]);
+	}
+
+	/// Access the instance.
+	TClass& operator*()
+	{
+		return *reinterpret_cast<TClass*>(&m_data[0]);
+	}
+
+	/// Access the instance.
+	const TClass& operator*() const
+	{
+		return *reinterpret_cast<const TClass*>(&m_data[0]);
+	}
+
+	/// Access the instance.
+	TClass* get()
+	{
+		return reinterpret_cast<TClass*>(&m_data[0]);
+	}
+
+	/// Access the instance.
+	const TClass* get() const
+	{
+		return reinterpret_cast<const TClass*>(&m_data[0]);
+	}
+
+private:
+	/// The data as a POD with correct size and alignment.
+	alignas(alignof(TClass)) Array<U8, sizeof(TClass)> m_data;
+};
+/// @}
+
+} // end namespace anki