diff options
Diffstat (limited to 'gfx/gl')
-rw-r--r-- | gfx/gl/program.h | 6 | ||||
-rw-r--r-- | gfx/gl/sceneProvider.cpp | 2 | ||||
-rw-r--r-- | gfx/gl/sceneRenderer.cpp | 28 | ||||
-rw-r--r-- | gfx/gl/sceneRenderer.h | 3 | ||||
-rw-r--r-- | gfx/gl/sceneShader.cpp | 2 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowDynamicPointStencil.fs | 16 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowDynamicPointStencil.gs | 36 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowDynamicPointStencil.vs | 17 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowStencil.fs | 18 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowStencil.gs | 28 | ||||
-rw-r--r-- | gfx/gl/shaders/shadowStencil.vs | 20 | ||||
-rw-r--r-- | gfx/gl/shadowMapper.cpp | 80 | ||||
-rw-r--r-- | gfx/gl/shadowMapper.h | 21 | ||||
-rw-r--r-- | gfx/gl/shadowStenciller.cpp | 74 | ||||
-rw-r--r-- | gfx/gl/shadowStenciller.h | 27 |
15 files changed, 345 insertions, 33 deletions
diff --git a/gfx/gl/program.h b/gfx/gl/program.h index c89a128..20be1aa 100644 --- a/gfx/gl/program.h +++ b/gfx/gl/program.h @@ -33,6 +33,12 @@ public: return location; } + explicit + operator bool() const + { + return location >= 0; + } + protected: GLint location; }; diff --git a/gfx/gl/sceneProvider.cpp b/gfx/gl/sceneProvider.cpp index 2e8604c..4e271db 100644 --- a/gfx/gl/sceneProvider.cpp +++ b/gfx/gl/sceneProvider.cpp @@ -5,7 +5,7 @@ void SceneProvider::environment(const SceneShader &, const SceneRenderer & renderer) const { renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); - renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {-1, 1, -1}, *this); + renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {{-quarter_pi, -quarter_pi}}, *this); } void diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index e0938f2..b2a7d78 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -62,7 +62,7 @@ SceneRenderer::render(const SceneProvider & scene) const shader.setViewProjection(camera.getPosition(), camera.getViewProjection()); glViewport(0, 0, size.x, size.y); - // Geometry pass + // Geometry/colour pass - writes albedo, normal and position textures glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); glEnable(GL_BLEND); glEnable(GL_CULL_FACE); @@ -73,7 +73,13 @@ SceneRenderer::render(const SceneProvider & scene) const glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); scene.content(shader); - // Illumination pass + // Environment pass - + // * ambient - clears illumination texture - see setAmbientLight + // * directional - updates shadowMapper, reads normal and position, writes illumination - see setDirectionalLight + scene.environment(shader, *this); + + // Scene lights pass - + // * per light - reads normal and position, writes illumination glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll); glBlendFunc(GL_ONE, GL_ONE); glActiveTexture(GL_TEXTURE0); @@ -82,11 +88,10 @@ SceneRenderer::render(const SceneProvider & scene) const glBindTexture(GL_TEXTURE_2D, gNormal); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D_ARRAY, shadowMapper); - scene.environment(shader, *this); glDisable(GL_DEPTH_TEST); scene.lights(shader); - // Lighting pass + // Composition pass - reads albedo and illumination, writes output glBindFramebuffer(GL_FRAMEBUFFER, output); glViewport(0, 0, size.x, size.y); glCullFace(GL_BACK); @@ -109,14 +114,22 @@ SceneRenderer::setAmbientLight(const RGB & colour) const } void -SceneRenderer::setDirectionalLight(const RGB & colour, const Direction3D & direction, const SceneProvider & scene) const +SceneRenderer::setDirectionalLight( + const RGB & colour, const LightDirection & direction, const SceneProvider & scene) const { if (colour.r > 0 || colour.g > 0 || colour.b > 0) { const auto lvp = shadowMapper.update(scene, direction, camera); glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll); + glBlendFunc(GL_ONE, GL_ONE); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, gPosition); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, gNormal); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D_ARRAY, shadowMapper); glViewport(0, 0, size.x, size.y); dirLight.use(); - dirLight.setDirectionalLight(colour, direction, camera.getPosition(), lvp); + dirLight.setDirectionalLight(colour, direction.vector(), camera.getPosition(), lvp); renderQuad(); } } @@ -142,8 +155,7 @@ SceneRenderer::DirectionalLightProgram::setDirectionalLight( return toTextureSpaceMat * m; }; glUniform(colourLoc, c); - const auto nd = glm::normalize(d); - glUniform(directionLoc, nd); + glUniform(directionLoc, d); glUniform(lightPointLoc, p); glUniform(lightViewProjectionCountLoc, static_cast<GLuint>(lvp.size())); glUniform(lightViewProjectionLoc, std::span<const glm::mat4> {lvp * toTextureSpace}); diff --git a/gfx/gl/sceneRenderer.h b/gfx/gl/sceneRenderer.h index 4195bcf..93470f5 100644 --- a/gfx/gl/sceneRenderer.h +++ b/gfx/gl/sceneRenderer.h @@ -1,6 +1,7 @@ #pragma once #include "camera.h" +#include "gfx/lightDirection.h" #include "glArrays.h" #include "program.h" #include "sceneProvider.h" @@ -14,7 +15,7 @@ public: void render(const SceneProvider &) const; void setAmbientLight(const RGB & colour) const; - void setDirectionalLight(const RGB & colour, const Direction3D & direction, const SceneProvider &) const; + void setDirectionalLight(const RGB & colour, const LightDirection & direction, const SceneProvider &) const; Camera camera; diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp index 4cbccb3..571538a 100644 --- a/gfx/gl/sceneShader.cpp +++ b/gfx/gl/sceneShader.cpp @@ -65,7 +65,7 @@ SceneShader::SceneProgram::setViewProjection(const GlobalPosition3D & viewPoint, void SceneShader::SceneProgram::setViewPort(const ViewPort & viewPort) const { - if (viewPortLoc >= 0) { + if (viewPortLoc) { glUseProgram(*this); glUniform(viewPortLoc, viewPort); } diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.fs b/gfx/gl/shaders/shadowDynamicPointStencil.fs new file mode 100644 index 0000000..fe91b07 --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.fs @@ -0,0 +1,16 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +layout(binding = 0) uniform sampler2DArray stencilDepth; +flat in vec3 scale; +in vec3 texCoord; + +void +main() +{ + float stDepth = texture(stencilDepth, texCoord).r; + if (stDepth >= 1) { + discard; + } + gl_FragDepth = gl_FragCoord.z + ((stDepth - 0.5) * scale.z); +} diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.gs b/gfx/gl/shaders/shadowDynamicPointStencil.gs new file mode 100644 index 0000000..7e81d97 --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.gs @@ -0,0 +1,36 @@ +#version 330 core +#extension GL_ARB_viewport_array : enable + +const vec2[] corners = vec2[4](vec2(-1, -1), vec2(-1, 1), vec2(1, -1), vec2(1, 1)); +const float tau = 6.28318531; + +uniform mat4 viewProjection[4]; +uniform int viewProjections; +uniform vec3 sizes[4]; +uniform float size; + +in float vmodelYaw[]; +in ivec3 vworldPos[]; + +flat out vec3 scale; +out vec3 texCoord; + +layout(points) in; +layout(triangle_strip, max_vertices = 16) out; + +void +main() +{ + int viewAngle = int(round(4.0 + (vmodelYaw[0] / tau))) % 8; + for (gl_Layer = 0; gl_Layer < viewProjections; ++gl_Layer) { + scale = 2.0 * size / sizes[gl_Layer]; + vec4 pos = viewProjection[gl_Layer] * vec4(vworldPos[0], 1); + for (int c = 0; c < corners.length(); ++c) { + gl_Position = pos + vec4(scale.xy * corners[c], 0, 0); + gl_Position.z = max(gl_Position.z, -1); + texCoord = vec3((corners[c] * 0.5) + 0.5, viewAngle); + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.vs b/gfx/gl/shaders/shadowDynamicPointStencil.vs new file mode 100644 index 0000000..0dd2d79 --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.vs @@ -0,0 +1,17 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +layout(location = 0) in ivec3 worldPos; +layout(location = 1) in float modelYaw; +uniform ivec3 viewPoint; +uniform vec3 centre; + +out float vmodelYaw; +out ivec3 vworldPos; + +void +main() +{ + vmodelYaw = modelYaw; + vworldPos = worldPos - viewPoint + ivec3(centre); +} diff --git a/gfx/gl/shaders/shadowStencil.fs b/gfx/gl/shaders/shadowStencil.fs new file mode 100644 index 0000000..1164cc9 --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.fs @@ -0,0 +1,18 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +layout(binding = 0) uniform sampler2D textureAlbedo; + +include(`materialDetail.glsl') +include(`materialCommon.glsl') +in vec2 gTexCoords; +flat in MaterialDetail gMaterial; + +void +main() +{ + if (getTextureColour(gMaterial, gTexCoords).a < 0.5) { + discard; + } + gl_FragDepth = gl_FragCoord.z; +} diff --git a/gfx/gl/shaders/shadowStencil.gs b/gfx/gl/shaders/shadowStencil.gs new file mode 100644 index 0000000..2c3f9bd --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.gs @@ -0,0 +1,28 @@ +#version 330 core +#extension GL_ARB_viewport_array : enable + +include(`materialDetail.glsl') + +layout(triangles) in; +layout(triangle_strip, max_vertices = 24) out; + +uniform mat4 viewProjection[8]; +in vec3 FragPos[]; +in vec2 TexCoords[]; +flat in MaterialDetail Material[]; +out vec2 gTexCoords; +flat out MaterialDetail gMaterial; + +void +main() +{ + for (gl_Layer = 0; gl_Layer < viewProjection.length(); ++gl_Layer) { + for (int v = 0; v < FragPos.length(); ++v) { + gl_Position = viewProjection[gl_Layer] * vec4(FragPos[v], 1); + gTexCoords = TexCoords[v]; + gMaterial = Material[v]; + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/gfx/gl/shaders/shadowStencil.vs b/gfx/gl/shaders/shadowStencil.vs new file mode 100644 index 0000000..a15c4fb --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.vs @@ -0,0 +1,20 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +layout(binding = 1) uniform usampler2DRect materialData; + +include(`meshIn.glsl') +include(`materialDetail.glsl') +include(`getMaterialDetail.glsl') + +out vec3 FragPos; +out vec2 TexCoords; +flat out MaterialDetail Material; + +void +main() +{ + TexCoords = texCoord; + Material = getMaterialDetail(material); + FragPos = position; +} diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index a846a3d..1b95aa3 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -1,13 +1,20 @@ #include "shadowMapper.h" #include "camera.h" #include "collections.h" +#include "game/gamestate.h" #include "gfx/gl/shaders/fs-shadowDynamicPointInstWithTextures.h" +#include "gfx/gl/shaders/fs-shadowDynamicPointStencil.h" #include "gfx/gl/shaders/gs-commonShadowPoint.h" #include "gfx/gl/shaders/gs-shadowDynamicPointInstWithTextures.h" +#include "gfx/gl/shaders/gs-shadowDynamicPointStencil.h" #include "gfx/gl/shaders/vs-shadowDynamicPoint.h" #include "gfx/gl/shaders/vs-shadowDynamicPointInst.h" #include "gfx/gl/shaders/vs-shadowDynamicPointInstWithTextures.h" +#include "gfx/gl/shaders/vs-shadowDynamicPointStencil.h" #include "gfx/gl/shaders/vs-shadowLandmass.h" +#include "gfx/gl/shadowStenciller.h" +#include "gfx/lightDirection.h" +#include "gfx/renderable.h" #include "gl_traits.h" #include "location.h" #include "maths.h" @@ -45,13 +52,15 @@ ShadowMapper::ShadowMapper(const TextureAbsCoord & s) : glBindFramebuffer(GL_FRAMEBUFFER, 0); } -constexpr std::array<GlobalDistance, ShadowMapper::SHADOW_BANDS + 1> shadowBands { - 1000, - 250000, - 750000, - 2500000, - 10000000, -}; +constexpr auto shadowBands + = []<GlobalDistance... ints>(const float scaleFactor, std::integer_sequence<GlobalDistance, ints...>) { + const auto base = 10'000'000 / pow(scaleFactor, sizeof...(ints) - 1); + return std::array {1, static_cast<GlobalDistance>((base * pow(scaleFactor, ints)))...}; + }(6.6F, std::make_integer_sequence<GlobalDistance, ShadowMapper::SHADOW_BANDS>()); + +static_assert(shadowBands.front() == 1); +static_assert(shadowBands.back() == 10'000'000); +static_assert(shadowBands.size() == ShadowMapper::SHADOW_BANDS + 1); std::vector<std::array<RelativePosition3D, 4>> ShadowMapper::getBandViewExtents(const Camera & camera, const glm::mat4 & lightViewDir) @@ -72,34 +81,49 @@ ShadowMapper::getBandViewExtents(const Camera & camera, const glm::mat4 & lightV } ShadowMapper::Definitions -ShadowMapper::update(const SceneProvider & scene, const Direction3D & dir, const Camera & camera) const +ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, const Camera & camera) const { + glCullFace(GL_FRONT); + glEnable(GL_DEPTH_TEST); + + shadowStenciller.setLightDirection(dir); + for (const auto & [id, asset] : gameState->assets) { + if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { + r->updateStencil(shadowStenciller); + } + } + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); - glCullFace(GL_FRONT); glViewport(0, 0, size.x, size.y); - const auto lightViewDir = glm::lookAt({}, dir, up); + const auto lightViewDir = glm::lookAt({}, dir.vector(), up); const auto lightViewPoint = camera.getPosition(); const auto bandViewExtents = getBandViewExtents(camera, lightViewDir); Definitions out; + Sizes sizes; std::transform(bandViewExtents.begin(), std::prev(bandViewExtents.end()), std::next(bandViewExtents.begin()), std::back_inserter(out), - [bands = bandViewExtents.size() - 2, &lightViewDir](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)); - }; + [bands = bandViewExtents.size() - 2, &lightViewDir, &sizes](const auto & near, const auto & far) mutable { + const auto extents_minmax + = [extents = std::span {near.begin(), far.end()}](auto && comp, RelativeDistance extra) { + const auto mm = std::minmax_element(extents.begin(), extents.end(), comp); + return std::make_pair(comp.get(*mm.first) - extra, comp.get(*mm.second) + extra); + }; + const std::array extents = {extents_minmax(CompareBy {0}, 0), extents_minmax(CompareBy {1}, 0), + extents_minmax(CompareBy {2}, 10'000)}; 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})); + }(extents[0], extents[1], extents[2]); + sizes.emplace_back(extents[0].second - extents[0].first, extents[1].second - extents[1].first, + extents[2].second - extents[2].first); return lightProjection * lightViewDir; }); for (const auto p : std::initializer_list<const ShadowProgram *> { - &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures}) { - p->setView(out, lightViewPoint); + &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures, &stencilShadowProgram}) { + p->setView(out, sizes, lightViewPoint); } scene.shadows(*this); @@ -116,12 +140,15 @@ ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs, const Shader & gs, } void -ShadowMapper::ShadowProgram::setView( - const std::span<const glm::mat4> viewProjection, const GlobalPosition3D viewPoint) const +ShadowMapper::ShadowProgram::setView(const std::span<const glm::mat4x4> viewProjection, + const std::span<const RelativePosition3D> sizes, const GlobalPosition3D viewPoint) const { use(); glUniform(viewPointLoc, viewPoint); glUniform(viewProjectionLoc, viewProjection); + if (sizesLoc) { + glUniform(sizesLoc, sizes); + } glUniform(viewProjectionsLoc, static_cast<GLint>(viewProjection.size())); } @@ -146,3 +173,16 @@ ShadowMapper::DynamicPoint::setModel(const Location & location) const glUniform(modelLoc, location.getRotationTransform()); glUniform(modelPosLoc, location.pos); } + +ShadowMapper::StencilShadowProgram::StencilShadowProgram() : + ShadowProgram {shadowDynamicPointStencil_vs, shadowDynamicPointStencil_gs, shadowDynamicPointStencil_fs} +{ +} + +void +ShadowMapper::StencilShadowProgram::use(const RelativePosition3D & centre, const float size) const +{ + Program::use(); + glUniform(centreLoc, centre); + glUniform(sizeLoc, size); +} diff --git a/gfx/gl/shadowMapper.h b/gfx/gl/shadowMapper.h index 73dadd0..951e29c 100644 --- a/gfx/gl/shadowMapper.h +++ b/gfx/gl/shadowMapper.h @@ -1,6 +1,7 @@ #pragma once #include "config/types.h" +#include "gfx/gl/shadowStenciller.h" #include "lib/glArrays.h" #include "program.h" #include <gfx/models/texture.h> @@ -10,6 +11,7 @@ class SceneProvider; class Camera; +class LightDirection; class ShadowMapper { public: @@ -18,20 +20,23 @@ public: static constexpr std::size_t SHADOW_BANDS {4}; using Definitions = std::vector<glm::mat4x4>; + using Sizes = std::vector<RelativePosition3D>; - [[nodiscard]] Definitions update(const SceneProvider &, const Direction3D & direction, const Camera &) const; + [[nodiscard]] Definitions update(const SceneProvider &, const LightDirection & direction, const Camera &) const; class ShadowProgram : public Program { public: explicit ShadowProgram(const Shader & vs); explicit ShadowProgram(const Shader & vs, const Shader & gs, const Shader & fs); - void setView(const std::span<const glm::mat4>, const GlobalPosition3D) const; + void setView(const std::span<const glm::mat4x4>, const std::span<const RelativePosition3D>, + const GlobalPosition3D) const; void use() const; private: RequiredUniformLocation viewProjectionLoc {*this, "viewProjection"}; RequiredUniformLocation viewProjectionsLoc {*this, "viewProjections"}; + UniformLocation sizesLoc {*this, "sizes"}; RequiredUniformLocation viewPointLoc {*this, "viewPoint"}; }; @@ -46,8 +51,19 @@ public: RequiredUniformLocation modelPosLoc {*this, "modelPos"}; }; + class StencilShadowProgram : public ShadowProgram { + public: + StencilShadowProgram(); + void use(const RelativePosition3D & centre, const float size) const; + + private: + RequiredUniformLocation centreLoc {*this, "centre"}; + RequiredUniformLocation sizeLoc {*this, "size"}; + }; + ShadowProgram landmess, dynamicPointInst, dynamicPointInstWithTextures; DynamicPoint dynamicPoint; + StencilShadowProgram stencilShadowProgram; // NOLINTNEXTLINE(hicpp-explicit-conversions) operator GLuint() const @@ -61,4 +77,5 @@ private: glFrameBuffer depthMapFBO; glTexture depthMap; TextureAbsCoord size; + mutable ShadowStenciller shadowStenciller; }; diff --git a/gfx/gl/shadowStenciller.cpp b/gfx/gl/shadowStenciller.cpp new file mode 100644 index 0000000..86b77e4 --- /dev/null +++ b/gfx/gl/shadowStenciller.cpp @@ -0,0 +1,74 @@ +#include "shadowStenciller.h" +#include "gfx/gl/program.h" +#include "gfx/gl/shaders/fs-shadowStencil.h" +#include "gfx/gl/shaders/gs-shadowStencil.h" +#include "gfx/gl/shaders/vs-shadowStencil.h" +#include "gfx/lightDirection.h" +#include "gfx/models/mesh.h" +#include "glArrays.h" +#include "gl_traits.h" +#include "maths.h" +#include <stdexcept> + +ShadowStenciller::ShadowStenciller() : + shadowCaster {shadowStencil_vs, shadowStencil_gs, shadowStencil_fs}, viewProjections {} +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void +ShadowStenciller::setLightDirection(const LightDirection & lightDir) +{ + viewProjections = [&lightDir]<GLint... Ep>(std::integer_sequence<GLint, Ep...>) { + constexpr float STEP = two_pi / STENCIL_ANGLES<decltype(two_pi)>; + return std::array {rotate_pitch<4>(half_pi - lightDir.position().y) + * rotate_yaw<4>((Ep * STEP) - lightDir.position().x)...}; + }(std::make_integer_sequence<GLint, STENCIL_ANGLES<GLint>>()); +} + +glTexture +ShadowStenciller::createStencilTexture(GLsizei width, GLsizei height) +{ + glTexture stencil; + glBindTexture(GL_TEXTURE_2D_ARRAY, stencil); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT, width, height, STENCIL_ANGLES<GLint>, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr); + + return stencil; +} + +void +ShadowStenciller::renderStencil(const glTexture & stencil, const MeshBase & mesh, const Texture::AnyPtr texture) const +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, stencil, 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw std::runtime_error("Stencil framebuffer not complete!"); + } + if (texture) { + texture->bind(); + } + glUseProgram(shadowCaster); + glClear(GL_DEPTH_BUFFER_BIT); + const auto stencilSize = Texture::getSize(stencil); + glViewport(0, 0, stencilSize.x, stencilSize.y); + const auto & centre = mesh.getDimensions().centre; + const auto & size = mesh.getDimensions().size; + glUniform(viewProjectionLoc, + std::span<const glm::mat4> {viewProjections * + [extentsMat = glm::translate(glm::ortho(-size, size, -size, size, -size, size), -centre)]( + const auto & viewProjection) { + return viewProjection * extentsMat; + }}); + mesh.Draw(); +} diff --git a/gfx/gl/shadowStenciller.h b/gfx/gl/shadowStenciller.h new file mode 100644 index 0000000..f774ac7 --- /dev/null +++ b/gfx/gl/shadowStenciller.h @@ -0,0 +1,27 @@ +#pragma once + +#include "gfx/gl/program.h" +#include "gfx/models/mesh.h" +#include "gfx/models/texture.h" +#include "glArrays.h" + +class LightDirection; + +class ShadowStenciller { +public: + template<typename T> static constexpr T STENCIL_ANGLES = 8; + + ShadowStenciller(); + + [[nodiscard]] + static glTexture createStencilTexture(GLsizei width, GLsizei height); + void setLightDirection(const LightDirection & lightDir); + void renderStencil(const glTexture &, const MeshBase &, Texture::AnyPtr texture) const; + +private: + glFrameBuffer fbo; + Program shadowCaster; + Program::RequiredUniformLocation viewProjectionLoc {shadowCaster, "viewProjection"}; + + std::array<glm::mat4, STENCIL_ANGLES<size_t>> viewProjections; +}; |