Browse Source

Add motion blur to skins

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
c69d0f4f82

+ 1 - 0
README.md

@@ -89,6 +89,7 @@ This code repository contains **3 sample projects** that are built by default (`
 - `sponza`: The Crytek's Sponza scene
 - `simple_scene`: A simple scene
 - `physics_playground`: A scene with programmer's art and some physics interactions
+- `skeletal_animation`: A simple scene with an animated skin
 
 You can try running them and interacting with them. To run sponza, for example, execute the binary from any working
 directory.

+ 6 - 0
samples/common/Framework.cpp

@@ -63,6 +63,12 @@ Error SampleApp::userMainLoop(Bool& quit)
 		setDisplayDeveloperConsole(!getDisplayDeveloperConsole());
 	}
 
+	if(in.getKey(KeyCode::Y) == 1)
+	{
+		renderer.setCurrentDebugRenderTarget(
+			(renderer.getCurrentDebugRenderTarget() == "GBuffer_velocity") ? "" : "GBuffer_velocity");
+	}
+
 	if(in.getKey(KeyCode::U) == 1)
 	{
 		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSGI") ? "" : "SSGI");

+ 2 - 0
samples/skeletal_animation/Main.cpp

@@ -33,6 +33,8 @@ public:
 
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f));
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setParticleDensity(2.0f);
+
+		getMainRenderer().getOffscreenRenderer().getBloom().setThreshold(5.0f);
 		return Error::NONE;
 	}
 

+ 20 - 0
samples/skeletal_animation/assets/scene.lua

@@ -11,6 +11,26 @@ trf:setRotation(rot)
 trf:setScale(1.000000)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
+node = scene:newGlobalIlluminationProbeNode("Cube")
+comp = node:getSceneNodeBase():getGlobalIlluminationProbeComponent()
+comp:setBoundingBox(Vec4.new(-9.643279, -9.643279, -9.643279, 0), Vec4.new(9.643279, 9.643279, 9.643279, 0))
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.057286, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newReflectionProbeNode("Cube.001", Vec4.new(-9.271889, -9.271889, -9.271889, 0), Vec4.new(9.271889, 9.271888, 9.271889, 0))
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.057286, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
 node = scene:newModelNode("room", "assets/room_room.ankimdl")
 trf = Transform.new()
 trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))

+ 2 - 2
shaders/GBufferCommon.glsl

@@ -47,7 +47,7 @@ layout(location = 5) out Vec3 out_eyeTangentSpace;
 layout(location = 6) out Vec3 out_normalTangentSpace;
 #		endif
 
-#		if ANKI_VELOCITY
+#		if ANKI_VELOCITY || ANKI_BONES
 layout(location = 7) out Vec2 out_velocity;
 #		endif
 #	endif // ANKI_PASS == PASS_GB
@@ -68,7 +68,7 @@ layout(location = 5) in Vec3 in_eyeTangentSpace;
 layout(location = 6) in Vec3 in_normalTangentSpace;
 #	endif
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 layout(location = 7) in Vec2 in_velocity;
 #	endif
 #endif

+ 27 - 5
shaders/GBufferGeneric.ankiprog

@@ -124,6 +124,11 @@ layout(set = 0, binding = 9, row_major, std140) readonly buffer b_ankiBoneTransf
 {
 	Mat4 u_ankiBoneTransforms[];
 };
+
+layout(set = 0, binding = 10, row_major, std140) readonly buffer b_ankiPrevFrameBoneTransforms
+{
+	Mat4 u_ankiPrevFrameBoneTransforms[];
+};
 #endif
 
 #if ANKI_INSTANCE_COUNT == 1
@@ -137,6 +142,7 @@ layout(set = 0, binding = 9, row_major, std140) readonly buffer b_ankiBoneTransf
 // Globals (always in local space)
 Vec3 g_position = in_position;
 #if ANKI_PASS == PASS_GB
+Vec3 g_prevPosition = in_position;
 Vec2 g_uv = in_uv;
 Vec3 g_normal = in_normal;
 Vec4 g_tangent = in_tangent;
@@ -147,16 +153,19 @@ Vec4 g_tangent = in_tangent;
 void skinning()
 {
 	Mat4 skinMat = u_ankiBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
+	Mat4 prevSkinMat = u_ankiPrevFrameBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
 	ANKI_UNROLL for(U32 i = 1; i < 4; ++i)
 	{
 		skinMat += u_ankiBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
+		prevSkinMat += u_ankiPrevFrameBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
 	}
 
-	g_position = (skinMat * Vec4(g_position, 1.0)).xyz;
 #	if ANKI_PASS == PASS_GB
+	g_prevPosition = (prevSkinMat * Vec4(g_position, 1.0)).xyz;
 	g_tangent.xyz = (skinMat * Vec4(g_tangent.xyz, 0.0)).xyz;
 	g_normal = (skinMat * Vec4(g_normal, 0.0)).xyz;
 #	endif
+	g_position = (skinMat * Vec4(g_position, 1.0)).xyz;
 }
 #endif
 
@@ -192,10 +201,23 @@ void parallax()
 }
 #endif
 
-#if ANKI_VELOCITY && ANKI_PASS == PASS_GB
+#if(ANKI_VELOCITY || ANKI_BONES) && ANKI_PASS == PASS_GB
 void velocity()
 {
-	const Vec4 v4 = u_ankiPerInstance[INSTANCE_ID].m_ankiPreviousMvp * Vec4(g_position, 1.0);
+#	if ANKI_BONES
+	const Vec3 prevLocalPos = g_prevPosition;
+#	else
+	const Vec3 prevLocalPos = g_position;
+#	endif
+
+#	if ANKI_VELOCITY
+	const Mat4 mvp = u_ankiPerInstance[INSTANCE_ID].m_ankiPreviousMvp;
+#	else
+	const Mat4 mvp = u_ankiPerInstance[INSTANCE_ID].m_ankiMvp;
+#	endif
+
+	const Vec4 v4 = mvp * Vec4(prevLocalPos, 1.0);
+
 	const Vec2 prevNdc = v4.xy / v4.w;
 
 	const Vec2 crntNdc = gl_Position.xy / gl_Position.w;
@@ -218,7 +240,7 @@ void main()
 	parallax();
 #	endif
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 	velocity();
 #	endif
 #else
@@ -362,7 +384,7 @@ void main()
 	const Vec3 emission = u_ankiPerDraw.m_emission;
 #	endif
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 	const Vec2 velocity = in_velocity;
 #	else
 	const Vec2 velocity = Vec2(-1.0);

+ 22 - 2
src/anki/renderer/Bloom.h

@@ -42,6 +42,26 @@ public:
 		return m_runCtx.m_upscaleRt;
 	}
 
+	F32 getThreshold() const
+	{
+		return m_exposure.m_threshold;
+	}
+
+	void setThreshold(F32 t)
+	{
+		m_exposure.m_threshold = t;
+	}
+
+	F32 getScale() const
+	{
+		return m_exposure.m_scale;
+	}
+
+	void setScale(F32 t)
+	{
+		m_exposure.m_scale = t;
+	}
+
 private:
 	static constexpr Format RT_PIXEL_FORMAT = Format::A2B10G10R10_UNORM_PACK32;
 
@@ -53,8 +73,8 @@ private:
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramPtr m_grProg;
 
-		F32 m_threshold = 10.0; ///< How bright it is
-		F32 m_scale = 1.0;
+		F32 m_threshold = 10.0f; ///< How bright it is
+		F32 m_scale = 1.0f;
 		U32 m_width = 0;
 		U32 m_height = 0;
 

+ 5 - 0
src/anki/renderer/GBuffer.h

@@ -23,6 +23,7 @@ public:
 	{
 		registerDebugRenderTarget("GBuffer_normals");
 		registerDebugRenderTarget("GBuffer_albedo");
+		registerDebugRenderTarget("GBuffer_velocity");
 	}
 
 	~GBuffer();
@@ -52,6 +53,10 @@ public:
 		{
 			handle = m_colorRts[2];
 		}
+		else if(rtName == "GBuffer_velocity")
+		{
+			handle = m_colorRts[3];
+		}
 		else
 		{
 			ANKI_ASSERT(!"See file");

+ 14 - 4
src/anki/resource/MaterialResource.cpp

@@ -386,18 +386,28 @@ Error MaterialResource::findBuiltinMutators()
 			{
 				if(block.m_set != m_descriptorSetIdx)
 				{
-					ANKI_RESOURCE_LOGE("The set of u_ankiBoneTransforms should be %u", m_descriptorSetIdx);
+					ANKI_RESOURCE_LOGE("The set of b_ankiBoneTransforms should be %u", m_descriptorSetIdx);
 					return Error::USER_DATA;
 				}
 
 				m_boneTrfsBinding = block.m_binding;
-				break;
+			}
+			else if(block.m_name.getBegin() == CString("b_ankiPrevFrameBoneTransforms"))
+			{
+				if(block.m_set != m_descriptorSetIdx)
+				{
+					ANKI_RESOURCE_LOGE("The set of b_ankiPrevFrameBoneTransforms should be %u", m_descriptorSetIdx);
+					return Error::USER_DATA;
+				}
+
+				m_prevFrameBoneTrfsBinding = block.m_binding;
 			}
 		}
 
-		if(m_boneTrfsBinding == MAX_U32)
+		if(m_boneTrfsBinding == MAX_U32 || m_prevFrameBoneTrfsBinding == MAX_U32)
 		{
-			ANKI_RESOURCE_LOGE("The program is using the %s mutator but b_ankiBoneTransforms was not found",
+			ANKI_RESOURCE_LOGE("The program is using the %s mutator but b_ankiBoneTransforms or "
+							   "b_ankiPrevFrameBoneTransforms was not found",
 				BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::BONES].cstr());
 			return Error::NONE;
 		}

+ 6 - 0
src/anki/resource/MaterialResource.h

@@ -340,6 +340,11 @@ public:
 		return m_boneTrfsBinding;
 	}
 
+	U32 getPrevFrameBoneTransformsBinding() const
+	{
+		return m_prevFrameBoneTrfsBinding;
+	}
+
 	U32 getUniformsBinding() const
 	{
 		ANKI_ASSERT(m_uboBinding != MAX_U32);
@@ -367,6 +372,7 @@ private:
 	U32 m_uboIdx = MAX_U32; ///< The b_ankiMaterial UBO inside the binary.
 	U32 m_uboBinding = MAX_U32;
 	U32 m_boneTrfsBinding = MAX_U32;
+	U32 m_prevFrameBoneTrfsBinding = MAX_U32;
 
 	/// Matrix of variants.
 	mutable Array5d<MaterialVariant, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2, 2> m_variantMatrix;

+ 1 - 0
src/anki/resource/ModelResource.cpp

@@ -51,6 +51,7 @@ void ModelPatch::getRenderingDataSub(
 		inf.m_program = variant.getShaderProgram();
 
 		inf.m_boneTransformsBinding = m_mtl->getBoneTransformsBinding();
+		inf.m_prevFrameBoneTransformsBinding = m_mtl->getPrevFrameBoneTransformsBinding();
 	}
 
 	// Vertex attributes & bindings

+ 1 - 0
src/anki/resource/ModelResource.h

@@ -80,6 +80,7 @@ public:
 	IndexType m_indexType;
 
 	U32 m_boneTransformsBinding;
+	U32 m_prevFrameBoneTransformsBinding;
 };
 
 /// Model patch interface class. Its very important class and it binds the material with the mesh

+ 15 - 3
src/anki/scene/ModelNode.cpp

@@ -187,10 +187,15 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		if(m_model->getSkeleton())
 		{
 			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
-			StagingGpuMemoryToken token;
+			const U32 boneCount = skinc.getBoneTransforms().getSize();
+			StagingGpuMemoryToken token, tokenPrev;
 			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(
-				skinc.getBoneTransforms().getSize() * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
-			memcpy(trfs, &skinc.getBoneTransforms()[0], skinc.getBoneTransforms().getSize() * sizeof(Mat4));
+				boneCount * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
+			memcpy(trfs, &skinc.getBoneTransforms()[0], boneCount * sizeof(Mat4));
+
+			trfs = ctx.m_stagingGpuAllocator->allocateFrame(
+				boneCount * sizeof(Mat4), StagingGpuMemoryType::STORAGE, tokenPrev);
+			memcpy(trfs, &skinc.getPreviousFrameBoneTransforms()[0], boneCount * sizeof(Mat4));
 
 			ANKI_ASSERT(modelInf.m_boneTransformsBinding < MAX_U32);
 			cmdb->bindStorageBuffer(patch.getMaterial()->getDescriptorSetIndex(),
@@ -198,6 +203,13 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 				token.m_buffer,
 				token.m_offset,
 				token.m_range);
+
+			ANKI_ASSERT(modelInf.m_prevFrameBoneTransformsBinding < MAX_U32);
+			cmdb->bindStorageBuffer(patch.getMaterial()->getDescriptorSetIndex(),
+				modelInf.m_prevFrameBoneTransformsBinding,
+				tokenPrev.m_buffer,
+				tokenPrev.m_offset,
+				tokenPrev.m_range);
 		}
 
 		// Program

+ 14 - 10
src/anki/scene/components/SkinComponent.cpp

@@ -19,19 +19,16 @@ SkinComponent::SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton)
 {
 	ANKI_ASSERT(node);
 
-	m_boneTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
-	m_animationTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
-
-	for(U32 i = 0; i < m_boneTrfs.getSize(); ++i)
-	{
-		m_boneTrfs[i].setIdentity();
-		m_animationTrfs[i] = {Vec3(0.0f), Quat::getIdentity(), 1.0f};
-	}
+	m_boneTrfs[0].create(m_node->getAllocator(), m_skeleton->getBones().getSize(), Mat4::getIdentity());
+	m_boneTrfs[1].create(m_node->getAllocator(), m_skeleton->getBones().getSize(), Mat4::getIdentity());
+	m_animationTrfs.create(
+		m_node->getAllocator(), m_skeleton->getBones().getSize(), {Vec3(0.0f), Quat::getIdentity(), 1.0f});
 }
 
 SkinComponent::~SkinComponent()
 {
-	m_boneTrfs.destroy(m_node->getAllocator());
+	m_boneTrfs[0].destroy(m_node->getAllocator());
+	m_boneTrfs[1].destroy(m_node->getAllocator());
 	m_animationTrfs.destroy(m_node->getAllocator());
 }
 
@@ -158,6 +155,9 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 
 	if(updated)
 	{
+		m_prevBoneTrfs = m_crntBoneTrfs;
+		m_crntBoneTrfs = m_crntBoneTrfs ^ 1;
+
 		// Walk the bone hierarchy to add additional transforms
 		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated, minExtend, maxExtend);
 
@@ -165,6 +165,10 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 		m_boneBoundingVolume.setMin(minExtend - E);
 		m_boneBoundingVolume.setMax(maxExtend + E);
 	}
+	else
+	{
+		m_prevBoneTrfs = m_crntBoneTrfs;
+	}
 
 	m_absoluteTime += dt;
 
@@ -186,7 +190,7 @@ void SkinComponent::visitBones(
 		outMat = parentTrf * bone.getTransform();
 	}
 
-	m_boneTrfs[bone.getIndex()] = outMat * bone.getVertexTransform();
+	m_boneTrfs[m_crntBoneTrfs][bone.getIndex()] = outMat * bone.getVertexTransform();
 
 	// Update volume
 	const Vec4 bonePos = outMat * Vec4(0.0f, 0.0f, 0.0f, 1.0f);

+ 11 - 3
src/anki/scene/components/SkinComponent.h

@@ -9,6 +9,7 @@
 #include <anki/resource/Forward.h>
 #include <anki/collision/Aabb.h>
 #include <anki/util/Forward.h>
+#include <anki/util/WeakArray.h>
 #include <anki/Math.h>
 
 namespace anki
@@ -49,9 +50,14 @@ public:
 
 	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 
-	const DynamicArray<Mat4>& getBoneTransforms() const
+	ConstWeakArray<Mat4> getBoneTransforms() const
 	{
-		return m_boneTrfs;
+		return m_boneTrfs[m_crntBoneTrfs];
+	}
+
+	ConstWeakArray<Mat4> getPreviousFrameBoneTransforms() const
+	{
+		return m_boneTrfs[m_prevBoneTrfs];
 	}
 
 	const SkeletonResourcePtr& getSkeleronResource() const
@@ -86,11 +92,13 @@ private:
 
 	SceneNode* m_node;
 	SkeletonResourcePtr m_skeleton;
-	DynamicArray<Mat4> m_boneTrfs;
+	Array<DynamicArray<Mat4>, 2> m_boneTrfs;
 	DynamicArray<Trf> m_animationTrfs;
 	Aabb m_boneBoundingVolume{Vec3(-1.0f), Vec3(1.0f)};
 	Array<Track, MAX_ANIMATION_TRACKS> m_tracks;
 	Second m_absoluteTime = 0.0;
+	U8 m_crntBoneTrfs = 0;
+	U8 m_prevBoneTrfs = 1;
 
 	void visitBones(const Bone& bone,
 		const Mat4& parentTrf,