From 839bf2ab51c4ec55f06b4224716c564451758072 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe 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 +++--- test/test-render.cpp | 10 +++--- 7 files changed, 80 insertions(+), 48 deletions(-) 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 +std::array 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 extentsAtDist(float) const; + std::array 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 lvp, const std::span shadowMapRegions) const + const std::span lvp, const std::span 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(lvp.size())); - glUniformMatrix4fv(lightViewProjectionLoc, static_cast(lvp.size()), GL_FALSE, glm::value_ptr(lvp.front())); - glUniform4fv(lightViewShadowMapRegionLoc, static_cast(shadowMapRegions.size()), - glm::value_ptr(shadowMapRegions.front())); + glUniform1ui(lightViewProjectionCountLoc, static_cast(maps)); + glUniformMatrix4fv(lightViewProjectionLoc, static_cast(maps), GL_FALSE, glm::value_ptr(lvp.front())); + glUniform4fv(lightViewShadowMapRegionLoc, static_cast(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 std::span) const; + const std::span, 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 #include #include +#include +#include ShadowMapper::ShadowMapper(const glm::ivec2 & s) : size {s} { @@ -55,7 +57,21 @@ constexpr std::array shadowBands { static_assert(viewports.size() == shadowMapRegions.size()); static_assert(shadowBands.size() == shadowMapRegions.size() + 1); -ShadowMapper::Definitions +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 viewExtents {bandViewExtents.front().begin(), bandViewExtents.back().end()}; + std::vector> 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 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 class ShadowMapper { public: explicit ShadowMapper(const glm::ivec2 & size); static constexpr std::size_t SHADOW_BANDS {4}; - template struct Definitions { - std::array projections; - std::array regions; + struct Definitions { + std::array projections; + std::array regions; + size_t maps {}; }; - Definitions 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: diff --git a/test/test-render.cpp b/test/test-render.cpp index 1cad732..09aa9fb 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -53,15 +53,15 @@ BOOST_DATA_TEST_CASE(cam, * boost::unit_test::data::xrange(50.F, 500.F, 70.F), dist, near, far) { - static constexpr glm::vec3 pos {-10, -10, 60}; + static constexpr glm::vec4 pos {-10, -10, 60, 0}; Camera cam {pos, half_pi, 1.f, near, far}; const auto e = cam.extentsAtDist(dist); - BOOST_CHECK_CLOSE_VEC(e[0], pos + glm::vec3(-dist, dist, -dist)); - BOOST_CHECK_CLOSE_VEC(e[1], pos + glm::vec3(-dist, dist, dist)); - BOOST_CHECK_CLOSE_VEC(e[2], pos + glm::vec3(dist, dist, -dist)); - BOOST_CHECK_CLOSE_VEC(e[3], pos + glm::vec3(dist, dist, dist)); + BOOST_CHECK_CLOSE_VEC(e[0], pos + glm::vec4(-dist, dist, -dist, dist)); + BOOST_CHECK_CLOSE_VEC(e[1], pos + glm::vec4(-dist, dist, dist, dist)); + BOOST_CHECK_CLOSE_VEC(e[2], pos + glm::vec4(dist, dist, -dist, dist)); + BOOST_CHECK_CLOSE_VEC(e[3], pos + glm::vec4(dist, dist, dist, dist)); } BOOST_FIXTURE_TEST_SUITE(w, TestRenderOutput); -- cgit v1.2.3