From 839bf2ab51c4ec55f06b4224716c564451758072 Mon Sep 17 00:00:00 2001
From: Dan Goodliffe <dan@randomdan.homeip.net>
Date: Sat, 7 Jan 2023 15:41:04 +0000
Subject: Only generate the first N shadow maps which are useful

Note: we don't yet optimise the use of the shadow map texture, each still renders into its own corner
---
 gfx/gl/camera.cpp        |  8 ++---
 gfx/gl/camera.h          |  2 +-
 gfx/gl/sceneRenderer.cpp | 12 +++----
 gfx/gl/sceneRenderer.h   |  2 +-
 gfx/gl/shadowMapper.cpp  | 84 ++++++++++++++++++++++++++++++++----------------
 gfx/gl/shadowMapper.h    | 10 +++---
 6 files changed, 75 insertions(+), 43 deletions(-)

(limited to 'gfx')

diff --git a/gfx/gl/camera.cpp b/gfx/gl/camera.cpp
index b52eca7..2ea326d 100644
--- a/gfx/gl/camera.cpp
+++ b/gfx/gl/camera.cpp
@@ -34,19 +34,19 @@ Camera::upFromForward(const glm::vec3 & forward)
 	return glm::cross(forward, right);
 }
 
-std::array<glm::vec3, 4>
+std::array<glm::vec4, 4>
 Camera::extentsAtDist(const float dist) const
 {
-	const auto clampToSeaFloor = [this](const glm::vec3 & target) {
+	const auto clampToSeaFloor = [this, dist](const glm::vec3 & target) {
 		if (target.z < -1.5f) {
 			const auto vec = glm::normalize(target - position);
 			constexpr glm::vec3 seafloor {0, 0, -1.5};
 			float outdist;
 			if (glm::intersectRayPlane(position, vec, seafloor, ::up, outdist)) {
-				return vec * outdist + position;
+				return (vec * outdist + position) ^ outdist;
 			}
 		}
-		return target;
+		return target ^ dist;
 	};
 	const auto depth = -(2.f * (dist - near) * far) / (dist * (near - far)) - 1.f;
 	static constexpr const std::array extents {-1.F, 1.F};
diff --git a/gfx/gl/camera.h b/gfx/gl/camera.h
index 5b2b4f4..94fd48b 100644
--- a/gfx/gl/camera.h
+++ b/gfx/gl/camera.h
@@ -61,7 +61,7 @@ public:
 		return position;
 	}
 
-	std::array<glm::vec3, 4> extentsAtDist(float) const;
+	std::array<glm::vec4, 4> extentsAtDist(float) const;
 
 	static glm::vec3 upFromForward(const glm::vec3 & forward);
 
diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp
index ee2b1ee..e7ce049 100644
--- a/gfx/gl/sceneRenderer.cpp
+++ b/gfx/gl/sceneRenderer.cpp
@@ -112,7 +112,7 @@ SceneRenderer::setDirectionalLight(
 		glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
 		glViewport(0, 0, size.x, size.y);
 		dirLight.use();
-		dirLight.setDirectionalLight(colour, direction, lvp.projections, lvp.regions);
+		dirLight.setDirectionalLight(colour, direction, lvp.projections, lvp.regions, lvp.maps);
 		renderQuad();
 	}
 }
@@ -135,13 +135,13 @@ SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() :
 
 void
 SceneRenderer::DirectionalLightProgram::setDirectionalLight(const glm::vec3 & c, const glm::vec3 & d,
-		const std::span<const glm::mat4x4> lvp, const std::span<const glm::vec4> shadowMapRegions) const
+		const std::span<const glm::mat4x4> lvp, const std::span<const glm::vec4> shadowMapRegions,
+		std::size_t maps) const
 {
 	glUniform3fv(colourLoc, 1, glm::value_ptr(c));
 	const auto nd = glm::normalize(d);
 	glUniform3fv(directionLoc, 1, glm::value_ptr(nd));
-	glUniform1ui(lightViewProjectionCountLoc, static_cast<GLuint>(lvp.size()));
-	glUniformMatrix4fv(lightViewProjectionLoc, static_cast<GLsizei>(lvp.size()), GL_FALSE, glm::value_ptr(lvp.front()));
-	glUniform4fv(lightViewShadowMapRegionLoc, static_cast<GLsizei>(shadowMapRegions.size()),
-			glm::value_ptr(shadowMapRegions.front()));
+	glUniform1ui(lightViewProjectionCountLoc, static_cast<GLuint>(maps));
+	glUniformMatrix4fv(lightViewProjectionLoc, static_cast<GLsizei>(maps), GL_FALSE, glm::value_ptr(lvp.front()));
+	glUniform4fv(lightViewShadowMapRegionLoc, static_cast<GLsizei>(maps), glm::value_ptr(shadowMapRegions.front()));
 }
diff --git a/gfx/gl/sceneRenderer.h b/gfx/gl/sceneRenderer.h
index 5f6a36e..d4af665 100644
--- a/gfx/gl/sceneRenderer.h
+++ b/gfx/gl/sceneRenderer.h
@@ -38,7 +38,7 @@ private:
 		using Program::use;
 
 		void setDirectionalLight(const glm::vec3 &, const glm::vec3 &, const std::span<const glm::mat4x4>,
-				const std::span<const glm::vec4>) const;
+				const std::span<const glm::vec4>, std::size_t maps) const;
 
 	private:
 		RequiredUniformLocation directionLoc, colourLoc, lightViewProjectionLoc, lightViewProjectionCountLoc,
diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp
index 325f7a0..2b3d774 100644
--- a/gfx/gl/shadowMapper.cpp
+++ b/gfx/gl/shadowMapper.cpp
@@ -11,6 +11,8 @@
 #include <glm/gtc/type_ptr.hpp>
 #include <glm/gtx/transform.hpp>
 #include <glm/matrix.hpp>
+#include <tuple>
+#include <vector>
 
 ShadowMapper::ShadowMapper(const glm::ivec2 & s) : size {s}
 {
@@ -55,7 +57,21 @@ constexpr std::array<float, ShadowMapper::SHADOW_BANDS + 1> shadowBands {
 static_assert(viewports.size() == shadowMapRegions.size());
 static_assert(shadowBands.size() == shadowMapRegions.size() + 1);
 
-ShadowMapper::Definitions<ShadowMapper::SHADOW_BANDS>
+struct DefinitionsInserter {
+	auto
+	operator++()
+	{
+		return out.maps++;
+	};
+	auto
+	operator*()
+	{
+		return std::tie(out.projections[out.maps], out.regions[out.maps]);
+	}
+	ShadowMapper::Definitions & out;
+};
+
+ShadowMapper::Definitions
 ShadowMapper::update(const SceneProvider & scene, const glm::vec3 & dir, const Camera & camera) const
 {
 	glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
@@ -63,35 +79,49 @@ ShadowMapper::update(const SceneProvider & scene, const glm::vec3 & dir, const C
 	glClear(GL_DEPTH_BUFFER_BIT);
 	glCullFace(GL_FRONT);
 
-	auto bandViewExtents = shadowBands * [&camera](auto distance) {
-		return camera.extentsAtDist(distance);
-	};
-	const std::span<glm::vec3> viewExtents {bandViewExtents.front().begin(), bandViewExtents.back().end()};
+	std::vector<std::array<glm::vec3, 4>> bandViewExtents;
+	for (const auto dist : shadowBands) {
+		const auto extents = camera.extentsAtDist(dist);
+		bandViewExtents.emplace_back(extents * [](const auto & e) -> glm::vec3 {
+			return e;
+		});
+		if (std::none_of(extents.begin(), extents.end(), [targetDist = dist * 0.99F](const glm::vec4 & e) {
+				return e.w > targetDist;
+			})) {
+			break;
+		}
+	}
 
 	const auto lightView = glm::lookAt(camera.getPosition(), camera.getPosition() + dir, up);
-	for (auto & e : viewExtents) {
-		e = lightView * glm::vec4(e, 1);
+	for (auto & band : bandViewExtents) {
+		for (auto & e : band) {
+			e = lightView * glm::vec4(e, 1);
+		}
 	}
-	Definitions<SHADOW_BANDS> out;
-	for (std::size_t band = 0; band < SHADOW_BANDS; ++band) {
-		const auto extents_minmax = [extents = viewExtents.subspan(band * 4, 8)](auto && comp) {
-			const auto mm = std::minmax_element(extents.begin(), extents.end(), comp);
-			return std::make_pair(comp.get(*mm.first), comp.get(*mm.second));
-		};
-
-		const auto lightProjection = [](const auto & x, const auto & y, const auto & z) {
-			return glm::ortho(x.first, x.second, y.first, y.second, -z.second, -z.first);
-		}(extents_minmax(CompareBy {0}), extents_minmax(CompareBy {1}), extents_minmax(CompareBy {2}));
-
-		out.projections[band] = lightProjection * lightView;
-		fixedPoint.setViewProjection(out.projections[band]);
-		dynamicPoint.setViewProjection(out.projections[band]);
-		out.regions[band] = shadowMapRegions[band];
-
-		const auto & viewport = viewports[band];
-		glViewport(size.x >> viewport.x, size.y >> viewport.y, size.x >> viewport.z, size.y >> viewport.w);
-		scene.shadows(*this);
-	};
+
+	Definitions out;
+	std::transform(bandViewExtents.begin(), std::prev(bandViewExtents.end()), std::next(bandViewExtents.begin()),
+			DefinitionsInserter {out},
+			[&scene, this, &lightView, band = 0U](const auto & near, const auto & far) mutable {
+				const auto extents_minmax = [extents = std::span {near.begin(), far.end()}](auto && comp) {
+					const auto mm = std::minmax_element(extents.begin(), extents.end(), comp);
+					return std::make_pair(comp.get(*mm.first), comp.get(*mm.second));
+				};
+
+				const auto lightProjection = [](const auto & x, const auto & y, const auto & z) {
+					return glm::ortho(x.first, x.second, y.first, y.second, -z.second, -z.first);
+				}(extents_minmax(CompareBy {0}), extents_minmax(CompareBy {1}), extents_minmax(CompareBy {2}));
+
+				const auto lightViewProjection = lightProjection * lightView;
+				fixedPoint.setViewProjection(lightViewProjection);
+				dynamicPoint.setViewProjection(lightViewProjection);
+
+				const auto & viewport = viewports[band];
+				glViewport(size.x >> viewport.x, size.y >> viewport.y, size.x >> viewport.z, size.y >> viewport.w);
+				scene.shadows(*this);
+
+				return std::make_pair(lightViewProjection, shadowMapRegions[band++]);
+			});
 
 	glCullFace(GL_BACK);
 
diff --git a/gfx/gl/shadowMapper.h b/gfx/gl/shadowMapper.h
index 0ce6898..bdbd39f 100644
--- a/gfx/gl/shadowMapper.h
+++ b/gfx/gl/shadowMapper.h
@@ -7,16 +7,18 @@
 class SceneProvider;
 class Camera;
 
+#include <gfx/models/texture.h>
 class ShadowMapper {
 public:
 	explicit ShadowMapper(const glm::ivec2 & size);
 
 	static constexpr std::size_t SHADOW_BANDS {4};
-	template<std::size_t S> struct Definitions {
-		std::array<glm::mat4x4, S> projections;
-		std::array<glm::vec4, S> regions;
+	struct Definitions {
+		std::array<glm::mat4x4, SHADOW_BANDS> projections;
+		std::array<glm::vec4, SHADOW_BANDS> regions;
+		size_t maps {};
 	};
-	Definitions<SHADOW_BANDS> update(const SceneProvider &, const glm::vec3 & direction, const Camera &) const;
+	Definitions update(const SceneProvider &, const glm::vec3 & direction, const Camera &) const;
 
 	class FixedPoint : public Program {
 	public:
-- 
cgit v1.2.3