Browse Source

Add more cnobes to the GI probes

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
e0c31eebb4

+ 74 - 41
shaders/LightShading.glslp

@@ -118,12 +118,12 @@ void main()
 			shadowFactor = 1.0;
 		}
 
-		Vec3 l = -u_dirLight.m_dir;
+		const Vec3 l = -u_dirLight.m_dir;
 
-		F32 lambert = max(gbuffer.m_subsurface, dot(l, gbuffer.m_normal));
+		const F32 lambert = max(gbuffer.m_subsurface, dot(l, gbuffer.m_normal));
 
-		Vec3 diffC = diffuseLambert(gbuffer.m_diffuse);
-		Vec3 specC = computeSpecularColorBrdf(gbuffer, viewDir, l);
+		const Vec3 diffC = diffuseLambert(gbuffer.m_diffuse);
+		const Vec3 specC = computeSpecularColorBrdf(gbuffer, viewDir, l);
 
 		out_color += (diffC + specC) * u_dirLight.m_diffuseColor * (shadowFactor * lambert);
 	}
@@ -138,7 +138,7 @@ void main()
 
 		ANKI_BRANCH if(light.m_shadowAtlasTileScale >= 0.0)
 		{
-			F32 shadow = computeShadowFactorPointLight(light, frag2Light, u_shadowTex, u_trilinearClampSampler);
+			const F32 shadow = computeShadowFactorPointLight(light, frag2Light, u_shadowTex, u_trilinearClampSampler);
 			lambert *= shadow;
 		}
 
@@ -152,12 +152,12 @@ void main()
 
 		LIGHTING_COMMON_BRDF();
 
-		F32 spot = computeSpotFactor(l, light.m_outerCos, light.m_innerCos, light.m_dir);
+		const F32 spot = computeSpotFactor(l, light.m_outerCos, light.m_innerCos, light.m_dir);
 
-		F32 shadowmapLayerIdx = light.m_shadowmapId;
+		const F32 shadowmapLayerIdx = light.m_shadowmapId;
 		ANKI_BRANCH if(shadowmapLayerIdx >= 0.0)
 		{
-			F32 shadow = computeShadowFactorSpotLight(light, worldPos, u_shadowTex, u_trilinearClampSampler);
+			const F32 shadow = computeShadowFactorSpotLight(light, worldPos, u_shadowTex, u_trilinearClampSampler);
 			lambert *= shadow;
 		}
 
@@ -171,67 +171,100 @@ void main()
 
 		const Vec3 reflDir = reflect(-viewDir, gbuffer.m_normal);
 		const F32 reflLod = F32(IR_MIPMAP_COUNT - 1u) * gbuffer.m_roughness;
-		F32 totalBlendWeight = EPSILON;
 
-		// Loop probes
-		ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+		if(subgroupAll(u_lightIndices[idxOffset] != MAX_U32 && u_lightIndices[idxOffset + 1u] == MAX_U32))
 		{
-			const ReflectionProbe probe = u_reflectionProbes[idx];
-			const Vec3 aabbMin = probe.m_aabbMin;
-			const Vec3 aabbMax = probe.m_aabbMax;
-			const Vec3 probeOrigin = probe.m_position;
-			const F32 cubemapIndex = probe.m_cubemapIndex;
-
-			// Compute blend weight
-			const F32 blendWeight = computeProbeBlendWeight(worldPos, aabbMin, aabbMax, 0.2);
-			totalBlendWeight += blendWeight;
-
-			// Sample reflections
-			Vec3 cubeUv = intersectProbe(worldPos, reflDir, aabbMin, aabbMax, probeOrigin);
-			Vec4 cubeArrUv = Vec4(cubeUv, cubemapIndex);
-			Vec3 c = textureLod(u_reflectionsTex, u_trilinearClampSampler, cubeArrUv, reflLod).rgb;
-			specIndirect += c * blendWeight;
+			// Only one probe, do a fast path without blend weight
+
+			const ReflectionProbe probe = u_reflectionProbes[u_lightIndices[idxOffset]];
+			idxOffset += 2u;
+
+			// Sample
+			const Vec3 cubeUv = intersectProbe(worldPos, reflDir, probe.m_aabbMin, probe.m_aabbMax, probe.m_position);
+			const Vec4 cubeArrUv = Vec4(cubeUv, probe.m_cubemapIndex);
+			specIndirect = textureLod(u_reflectionsTex, u_trilinearClampSampler, cubeArrUv, reflLod).rgb;
 		}
+		else
+		{
+			// Zero or more than one probes, do a slow path that blends them together
 
-		// Normalize the colors
-		specIndirect /= totalBlendWeight;
+			F32 totalBlendWeight = EPSILON;
+
+			// Loop probes
+			ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+			{
+				const ReflectionProbe probe = u_reflectionProbes[idx];
+
+				// Compute blend weight
+				const F32 blendWeight = computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, 0.2);
+				totalBlendWeight += blendWeight;
+
+				// Sample reflections
+				const Vec3 cubeUv =
+					intersectProbe(worldPos, reflDir, probe.m_aabbMin, probe.m_aabbMax, probe.m_position);
+				const Vec4 cubeArrUv = Vec4(cubeUv, probe.m_cubemapIndex);
+				Vec3 c = textureLod(u_reflectionsTex, u_trilinearClampSampler, cubeArrUv, reflLod).rgb;
+				specIndirect += c * blendWeight;
+			}
+
+			// Normalize the colors
+			specIndirect /= totalBlendWeight;
+		}
 
 		// Read the SSL result
 		const Vec4 ssr = textureLod(u_ssrRt, u_trilinearClampSampler, in_uv, 0.0);
 
 		// Combine the SSR and probe reflections and write the result
-		const Vec3 finalRefl = specIndirect * ssr.a + ssr.rgb;
+		const Vec3 finalSpecIndirect = specIndirect * ssr.a + ssr.rgb;
 
 		// Compute env BRDF
 		const F32 NoV = max(EPSILON, dot(gbuffer.m_normal, viewDir));
 		const Vec3 env =
 			envBRDF(gbuffer.m_specular, gbuffer.m_roughness, u_integrationLut, u_trilinearClampSampler, NoV);
 
-		out_color += finalRefl * env;
+		out_color += finalSpecIndirect * env;
 	}
 
 	// Indirect diffuse
 	{
 		Vec3 diffIndirect = Vec3(0.0);
-		F32 totalBlendWeight = EPSILON;
 
-		// Loop probes
-		ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+		if(subgroupAll(u_lightIndices[idxOffset] != MAX_U32 && u_lightIndices[idxOffset + 1u] == MAX_U32))
 		{
-			GlobalIlluminationProbe probe = u_giProbes[idx];
+			// Only one probe, do a fast path without blend weight
 
-			// Compute blend weight
-			const F32 blendWeight = computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, 0.2);
-			totalBlendWeight += blendWeight;
+			GlobalIlluminationProbe probe = u_giProbes[u_lightIndices[idxOffset]];
+			idxOffset += 2u;
 
 			// Sample
-			const Vec3 c = sampleGlobalIllumination(
+			diffIndirect = sampleGlobalIllumination(
 				worldPos, gbuffer.m_normal, probe, u_globalIlluminationTextures, u_trilinearClampSampler);
-			diffIndirect += c * blendWeight;
 		}
+		else
+		{
+			// Zero or more than one probes, do a slow path that blends them together
+
+			F32 totalBlendWeight = EPSILON;
 
-		// Normalize
-		diffIndirect /= totalBlendWeight;
+			// Loop probes
+			ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+			{
+				GlobalIlluminationProbe probe = u_giProbes[idx];
+
+				// Compute blend weight
+				const F32 blendWeight =
+					computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, probe.m_fadeDistance);
+				totalBlendWeight += blendWeight;
+
+				// Sample
+				const Vec3 c = sampleGlobalIllumination(
+					worldPos, gbuffer.m_normal, probe, u_globalIlluminationTextures, u_trilinearClampSampler);
+				diffIndirect += c * blendWeight;
+			}
+
+			// Normalize
+			diffIndirect /= totalBlendWeight;
+		}
 
 		out_color += diffIndirect * gbuffer.m_diffuse;
 	}

+ 7 - 1
shaders/glsl_cpp_common/ClusteredShading.h

@@ -113,8 +113,14 @@ struct GlobalIlluminationProbe
 
 	Vec3 m_aabbMax;
 	F32 m_halfTexelSizeU; // (1.0 / textureSize(texArr[m_textureIndex]).x) / 2.0
+
+	// Used to calculate a factor that is zero when fragPos is close to AABB bounds and 1.0 at m_fadeDistance and less.
+	F32 m_fadeDistance;
+	F32 m_padding0;
+	F32 m_padding1;
+	F32 m_padding2;
 };
-const U32 SIZEOF_GLOBAL_ILLUMINATION_PROBE = 2u * SIZEOF_VEC4;
+const U32 SIZEOF_GLOBAL_ILLUMINATION_PROBE = 3u * SIZEOF_VEC4;
 ANKI_SHADER_STATIC_ASSERT(sizeof(GlobalIlluminationProbe) == SIZEOF_GLOBAL_ILLUMINATION_PROBE)
 
 // Common uniforms for light shading passes

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

@@ -186,7 +186,8 @@ public:
 	Vec3 m_aabbMax;
 	UVec3 m_cellCounts;
 	U32 m_totalCellCount;
-	Vec3 m_cellSizes; ///< The cells might not be cubes.
+	Vec3 m_cellSizes; ///< The cells might not be cubes so have different sizes per dimension.
+	F32 m_fadeDistance;
 
 	GlobalIlluminationProbeQueueElement()
 	{

+ 14 - 1
src/anki/scene/components/GlobalIlluminationProbeComponent.h

@@ -63,6 +63,17 @@ public:
 		return m_aabbMax.xyz0();
 	}
 
+	F32 getFadeDistance() const
+	{
+		return m_fadeDistance;
+	}
+
+	void setFadeDistance(F32 dist)
+	{
+		ANKI_ASSERT(dist >= 0.0f);
+		m_fadeDistance = dist;
+	}
+
 	/// Returns true if it's marked for update this frame.
 	Bool getMarkedForRendering() const
 	{
@@ -89,6 +100,7 @@ public:
 		el.m_cellCounts = m_cellCounts;
 		el.m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
 		el.m_cellSizes = (m_aabbMax - m_aabbMin) / Vec3(m_cellCounts);
+		el.m_fadeDistance = m_fadeDistance;
 	}
 
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
@@ -104,8 +116,9 @@ private:
 	Vec3 m_aabbMin = Vec3(-1.0f);
 	Vec3 m_aabbMax = Vec3(+1.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
-	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	UVec3 m_cellCounts = UVec3(2u);
+	F32 m_cellSize = 4.0f; ///< Cell size in meters.
+	F32 m_fadeDistance = 0.2f;
 	Bool m_markedForRendering = false;
 	Bool m_dirty = true;
 

+ 96 - 0
src/anki/script/Scene.cpp

@@ -2217,6 +2217,100 @@ static int wrapGlobalIlluminationProbeComponentgetCellSize(lua_State* l)
 	return 0;
 }
 
+/// Pre-wrap method GlobalIlluminationProbeComponent::setFadeDistance.
+static inline int pwrapGlobalIlluminationProbeComponentsetFadeDistance(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	if(ANKI_UNLIKELY(LuaBinder::checkArgsCount(l, 2)))
+	{
+		return -1;
+	}
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, luaUserDataTypeInfoGlobalIlluminationProbeComponent, ud))
+	{
+		return -1;
+	}
+
+	GlobalIlluminationProbeComponent* self = ud->getData<GlobalIlluminationProbeComponent>();
+
+	// Pop arguments
+	F32 arg0;
+	if(ANKI_UNLIKELY(LuaBinder::checkNumber(l, 2, arg0)))
+	{
+		return -1;
+	}
+
+	// Call the method
+	self->setFadeDistance(arg0);
+
+	return 0;
+}
+
+/// Wrap method GlobalIlluminationProbeComponent::setFadeDistance.
+static int wrapGlobalIlluminationProbeComponentsetFadeDistance(lua_State* l)
+{
+	int res = pwrapGlobalIlluminationProbeComponentsetFadeDistance(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
+/// Pre-wrap method GlobalIlluminationProbeComponent::getFadeDistance.
+static inline int pwrapGlobalIlluminationProbeComponentgetFadeDistance(lua_State* l)
+{
+	LuaUserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	PtrSize size;
+	(void)size;
+
+	if(ANKI_UNLIKELY(LuaBinder::checkArgsCount(l, 1)))
+	{
+		return -1;
+	}
+
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, luaUserDataTypeInfoGlobalIlluminationProbeComponent, ud))
+	{
+		return -1;
+	}
+
+	GlobalIlluminationProbeComponent* self = ud->getData<GlobalIlluminationProbeComponent>();
+
+	// Call the method
+	F32 ret = self->getFadeDistance();
+
+	// Push return value
+	lua_pushnumber(l, ret);
+
+	return 1;
+}
+
+/// Wrap method GlobalIlluminationProbeComponent::getFadeDistance.
+static int wrapGlobalIlluminationProbeComponentgetFadeDistance(lua_State* l)
+{
+	int res = pwrapGlobalIlluminationProbeComponentgetFadeDistance(l);
+	if(res >= 0)
+	{
+		return res;
+	}
+
+	lua_error(l);
+	return 0;
+}
+
 /// Wrap class GlobalIlluminationProbeComponent.
 static inline void wrapGlobalIlluminationProbeComponent(lua_State* l)
 {
@@ -2228,6 +2322,8 @@ static inline void wrapGlobalIlluminationProbeComponent(lua_State* l)
 		l, "getAlignedBoundingBoxMax", wrapGlobalIlluminationProbeComponentgetAlignedBoundingBoxMax);
 	LuaBinder::pushLuaCFuncMethod(l, "setCellSize", wrapGlobalIlluminationProbeComponentsetCellSize);
 	LuaBinder::pushLuaCFuncMethod(l, "getCellSize", wrapGlobalIlluminationProbeComponentgetCellSize);
+	LuaBinder::pushLuaCFuncMethod(l, "setFadeDistance", wrapGlobalIlluminationProbeComponentsetFadeDistance);
+	LuaBinder::pushLuaCFuncMethod(l, "getFadeDistance", wrapGlobalIlluminationProbeComponentgetFadeDistance);
 	lua_settop(l, 0);
 }
 

+ 8 - 0
src/anki/script/Scene.xml

@@ -278,6 +278,14 @@ using WeakArraySceneNodePtr = WeakArray<SceneNode*>;
 				<method name="getCellSize">
 					<return>F32</return>
 				</method>
+				<method name="setFadeDistance">
+					<args>
+						<arg>F32</arg>
+					</args>
+				</method>
+				<method name="getFadeDistance">
+					<return>F32</return>
+				</method>
 			</methods>
 		</class>
 

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit 903a4e96a27bb0ba522d8e81634701f898d62736
+Subproject commit d21b2b95f33205b3f39b0d7c9729c2e205646acb

+ 4 - 2
tools/blender_anki_additions.patch

@@ -122,7 +122,7 @@ index d33ce725e58..bf36943b324 100644
  	ep.addProfileElements();
  	bool twoSided = false;
 diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp
-index 4b693332715..b554d7e2422 100644
+index 4b693332715..04fb3ee71f3 100644
 --- a/source/blender/collada/GeometryExporter.cpp
 +++ b/source/blender/collada/GeometryExporter.cpp
 @@ -47,6 +47,7 @@ extern "C" {
@@ -133,7 +133,7 @@ index 4b693332715..b554d7e2422 100644
  }
  
  #include "collada_internal.h"
-@@ -154,12 +155,133 @@ void GeometryExporter::operator()(Object *ob)
+@@ -154,12 +155,135 @@ void GeometryExporter::operator()(Object *ob)
  		}
  	}
  
@@ -148,6 +148,8 @@ index 4b693332715..b554d7e2422 100644
 +			"skip",
 +			"reflection_probe",
 +			"gi_probe",
++			"gi_probe_fade_distance",
++			"gi_probe_cell_size",
 +			"reflection_proxy",
 +			"occluder",
 +			"collision_mesh",

+ 27 - 3
tools/scene/Exporter.cpp

@@ -793,6 +793,8 @@ void Exporter::visitNode(const aiNode* ainode)
 		std::string lod1MeshName;
 		std::string collisionMesh;
 		bool special = false;
+		GiProbe giProbe;
+		bool isGiProbe = false;
 		for(const auto& prop : m_scene->mMeshes[meshIndex]->mProperties)
 		{
 			if(prop.first == "particles")
@@ -835,7 +837,7 @@ void Exporter::visitNode(const aiNode* ainode)
 			}
 			else if(prop.first == "gi_probe" && prop.second == "true")
 			{
-				GiProbe probe;
+				GiProbe& probe = giProbe;
 				aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
 				probe.m_position = aiVector3D(trf.a4, trf.b4, trf.c4);
 
@@ -846,10 +848,17 @@ void Exporter::visitNode(const aiNode* ainode)
 				probe.m_aabbMin = probe.m_position - half - probe.m_position;
 				probe.m_aabbMax = probe.m_position + half - probe.m_position;
 
-				m_giProbes.push_back(probe);
-
+				isGiProbe = true;
 				special = true;
 			}
+			else if(prop.first == "gi_probe_fade_distance")
+			{
+				giProbe.m_fadeDistance = std::strtof(prop.second.c_str(), nullptr);
+			}
+			else if(prop.first == "gi_probe_cell_size")
+			{
+				giProbe.m_cellSize = std::strtof(prop.second.c_str(), nullptr);
+			}
 			else if(prop.first == "reflection_proxy" && prop.second == "true")
 			{
 				ReflectionProxy proxy;
@@ -938,6 +947,11 @@ void Exporter::visitNode(const aiNode* ainode)
 			}
 		}
 
+		if(isGiProbe)
+		{
+			m_giProbes.push_back(giProbe);
+		}
+
 		if(special)
 		{
 			continue;
@@ -1063,6 +1077,16 @@ void Exporter::exportAll()
 			 << probe.m_aabbMin.z << ", 0), Vec4.new(" << probe.m_aabbMax.x << ", " << probe.m_aabbMax.y << ", "
 			 << probe.m_aabbMax.z << ", 0))\n";
 
+		if(probe.m_fadeDistance >= 0.0f)
+		{
+			file << "comp:setFadeDistance(" << probe.m_fadeDistance << ")\n";
+		}
+
+		if(probe.m_cellSize > 0.0f)
+		{
+			file << "comp:setCellSize(" << probe.m_cellSize << ")\n";
+		}
+
 		aiMatrix4x4 trf;
 		aiMatrix4x4::Translation(probe.m_position, trf);
 		writeNodeTransform("node", trf);

+ 9 - 1
tools/scene/Exporter.h

@@ -72,7 +72,15 @@ public:
 	aiVector3D m_aabbMax;
 };
 
-using GiProbe = ReflectionProbe;
+class GiProbe
+{
+public:
+	aiVector3D m_position;
+	aiVector3D m_aabbMin;
+	aiVector3D m_aabbMax;
+	float m_fadeDistance = -1.0f;
+	float m_cellSize = -1.0f;
+};
 
 class ReflectionProxy
 {