diff options
Diffstat (limited to 'gfx/gl')
32 files changed, 534 insertions, 136 deletions
diff --git a/gfx/gl/program.cpp b/gfx/gl/program.cpp index 7287fde..fdd4c6f 100644 --- a/gfx/gl/program.cpp +++ b/gfx/gl/program.cpp @@ -10,10 +10,10 @@ void Program::linkAndValidate() const { glLinkProgram(m_program); - Shader::CheckShaderError(m_program, GL_LINK_STATUS, true, "Error linking shader program"); + checkProgramError(m_program, GL_LINK_STATUS, "Error linking shader program"); glValidateProgram(m_program); - Shader::CheckShaderError(m_program, GL_VALIDATE_STATUS, true, "Invalid shader program"); + checkProgramError(m_program, GL_VALIDATE_STATUS, "Invalid shader program"); } void @@ -22,6 +22,21 @@ Program::use() const glUseProgram(m_program); } +void +Program::checkProgramError(GLuint program, GLuint flag, std::string_view errorMessage) const +{ + GLint success = 0; + + glGetProgramiv(program, flag, &success); + + if (success == GL_FALSE) { + std::array<GLchar, 1024> error {}; + glGetProgramInfoLog(program, error.size(), nullptr, error.data()); + + throw std::runtime_error {std::format("{}: '{}'", errorMessage, error.data())}; + } +} + Program::UniformLocation::UniformLocation(GLuint program, const char * name) : location {glGetUniformLocation(program, name)} { diff --git a/gfx/gl/program.h b/gfx/gl/program.h index 1a1c306..20be1aa 100644 --- a/gfx/gl/program.h +++ b/gfx/gl/program.h @@ -1,6 +1,6 @@ #pragma once -#include "shader.h" +#include "shader.h" // IWYU pragma: export #include <glRef.h> #include <glad/gl.h> #include <glm/mat4x4.hpp> @@ -12,6 +12,8 @@ using ProgramRef = glRef<GLuint, &glCreateProgram, &glDeleteProgram>; class Program { public: + Program() = delete; + template<typename... S> explicit Program(const S &... srcs) { (glAttachShader(m_program, srcs.compile()), ...); @@ -31,6 +33,12 @@ public: return location; } + explicit + operator bool() const + { + return location >= 0; + } + protected: GLint location; }; @@ -47,6 +55,7 @@ public: } protected: + void checkProgramError(GLuint program, GLuint flag, std::string_view errorMessage) const; void use() const; void linkAndValidate() const; ProgramRef m_program; 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/shader.cpp b/gfx/gl/shader.cpp index 0bc127a..9a4c270 100644 --- a/gfx/gl/shader.cpp +++ b/gfx/gl/shader.cpp @@ -1,4 +1,5 @@ #include "shader.h" +#include "msgException.h" #include <algorithm> #include <array> #include <format> @@ -18,6 +19,39 @@ namespace { constexpr std::array<std::tuple<std::string_view, GLenum, LookUpFunction>, 1> LOOKUPS {{ {"GL_MAX_GEOMETRY_OUTPUT_VERTICES", GL_MAX_GEOMETRY_OUTPUT_VERTICES, getInt}, }}; + + struct ShaderCompileError : public MsgException<std::invalid_argument> { + explicit ShaderCompileError(GLuint shader, Shader::Source src) : + MsgException<std::invalid_argument> {"Error compiling shader"}, shader {shader}, source {src}, + msg {getShaderText(GL_INFO_LOG_LENGTH, glGetShaderInfoLog)} + { + } + + [[nodiscard]] std::string + getMsg() const noexcept override + { + return std::format("Error compiling shader: '{}'\nSource:\n{}", + getShaderText(GL_INFO_LOG_LENGTH, glGetShaderInfoLog), source); + } + + private: + std::string + getShaderText(GLenum param, auto getTextFunc) const + { + std::string text; + text.resize_and_overwrite(static_cast<size_t>(Shader::getShaderParam(shader, param)), + [this, getTextFunc](auto buf, auto len) { + GLsizei outLen {}; + getTextFunc(shader, static_cast<GLsizei>(len), &outLen, buf); + return outLen; + }); + return text; + } + + const GLuint shader; + const Shader::Source source; + const std::string msg; + }; } Shader::ShaderRef @@ -29,8 +63,8 @@ Shader::compile() const }; if (lookups) { std::basic_string<GLchar> textMod {text}; - for (const auto & match : ctre::range<R"(\bGL_[A-Z_]+\b)">(textMod)) { - if (const auto lookup = std::find_if(LOOKUPS.begin(), LOOKUPS.end(), + for (const auto & match : ctre::search_all<R"(\bGL_[A-Z_]+\b)">(textMod)) { + if (const auto * const lookup = std::find_if(LOOKUPS.begin(), LOOKUPS.end(), [&match](const auto & lookup) { return std::get<std::string_view>(lookup) == match; }); @@ -46,31 +80,22 @@ Shader::compile() const } glCompileShader(shader); - CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error compiling shader!"); + checkShaderError(shader); return shader; } void -Shader::CheckShaderError(GLuint shader, GLuint flag, bool isProgram, std::string_view errorMessage) +Shader::checkShaderError(GLuint shader) const { - GLint success = 0; - - if (isProgram) { - glGetProgramiv(shader, flag, &success); + if (getShaderParam(shader, GL_COMPILE_STATUS) == GL_FALSE) { + throw ShaderCompileError {shader, text}; } - else { - glGetShaderiv(shader, flag, &success); - } - - if (success == GL_FALSE) { - std::array<GLchar, 1024> error {}; - if (isProgram) { - glGetProgramInfoLog(shader, error.size(), nullptr, error.data()); - } - else { - glGetShaderInfoLog(shader, error.size(), nullptr, error.data()); - } +} - throw std::runtime_error {std::format("{}: '{}'", errorMessage, error.data())}; - } +GLint +Shader::getShaderParam(GLuint shader, GLenum pname) +{ + GLint pvalue {}; + glGetShaderiv(shader, pname, &pvalue); + return pvalue; } diff --git a/gfx/gl/shader.h b/gfx/gl/shader.h index c6b45af..ce97734 100644 --- a/gfx/gl/shader.h +++ b/gfx/gl/shader.h @@ -7,6 +7,7 @@ class Shader { public: + using Source = std::basic_string_view<GLchar>; using ShaderRef = glRef<GLuint, &glCreateShader, &glDeleteShader>; constexpr Shader(const GLchar * text, GLuint type) : @@ -15,10 +16,13 @@ public: } [[nodiscard]] ShaderRef compile() const; - static void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, std::string_view errorMessage); + + [[nodiscard]] static GLint getShaderParam(GLuint shader, GLenum pname); private: - const std::basic_string_view<GLchar> text; + void checkShaderError(GLuint shader) const; + + const Source text; GLuint type; bool lookups; }; diff --git a/gfx/gl/shaders/commonPoint.glsl b/gfx/gl/shaders/commonPoint.glsl index 2d9e388..dc534d5 100644 --- a/gfx/gl/shaders/commonPoint.glsl +++ b/gfx/gl/shaders/commonPoint.glsl @@ -1,17 +1,4 @@ -layout(binding = 1) uniform usampler2DRect materialData; - -MaterialDetail -getMaterialDetail(uint midx) -{ - if (midx > 0u) { - const vec4 sPosSize = texture(materialData, uvec2(0, midx - 1u)); - const uvec4 sMode = texture(materialData, uvec2(1, midx - 1u)); - const uint mapmodeU = sMode.x & 0xFu; - const uint mapmodeV = (sMode.x & 0xF0u) >> 1; - return MaterialDetail(sPosSize.xy, sPosSize.zw, uvec2(mapmodeU, mapmodeV)); - } - return MaterialDetail(vec2(0, 0), vec2(0, 0), uvec2(0, 0)); -} +include(`getMaterialDetail.glsl') void main() diff --git a/gfx/gl/shaders/commonShadowPoint.glsl b/gfx/gl/shaders/commonShadowPoint.glsl index c4ea827..9910d46 100644 --- a/gfx/gl/shaders/commonShadowPoint.glsl +++ b/gfx/gl/shaders/commonShadowPoint.glsl @@ -1,11 +1,10 @@ out vec4 vworldPos; -ifdef(`TEXTURES', out vec2 vtexCoord;); - void main() { vec3 worldPos = model * position; vworldPos = vec4(worldPos - viewPoint + modelPos, 1); - ifdef(`TEXTURES', vtexCoord = texCoord;); + ifdef(`TEXTURES', TexCoords = texCoord;); + ifdef(`TEXTURES', Material = getMaterialDetail(material);); } diff --git a/gfx/gl/shaders/commonShadowPoint.gs b/gfx/gl/shaders/commonShadowPoint.gs index b99bd20..2413cc0 100644 --- a/gfx/gl/shaders/commonShadowPoint.gs +++ b/gfx/gl/shaders/commonShadowPoint.gs @@ -1,13 +1,16 @@ #version 330 core #extension GL_ARB_viewport_array : enable +ifdef(`TEXTURES', include(`materialDetail.glsl')) + uniform mat4 viewProjection[4]; uniform int viewProjections; in vec4 vworldPos[]; layout(triangles) in; layout(triangle_strip, max_vertices = 12) out; -ifdef(`TEXTURES', in vec2 vtexCoord[]; out vec2 texCoord;); +ifdef(`TEXTURES', in vec2 TexCoords[]; out vec2 texCoord;) +ifdef(`TEXTURES', flat in MaterialDetail Material[]; flat out MaterialDetail material;) void main() @@ -17,7 +20,8 @@ main() gl_Position = viewProjection[vp] * vworldPos[v]; gl_Position.z = max(gl_Position.z, -1); gl_Layer = vp; - ifdef(`TEXTURES', texCoord = vtexCoord[v];); + ifdef(`TEXTURES', texCoord = TexCoords[v];) + ifdef(`TEXTURES', material = Material[v];) EmitVertex(); } EndPrimitive(); diff --git a/gfx/gl/shaders/directionalLight.fs b/gfx/gl/shaders/directionalLight.fs index 24457b8..cdf0389 100644 --- a/gfx/gl/shaders/directionalLight.fs +++ b/gfx/gl/shaders/directionalLight.fs @@ -17,27 +17,38 @@ uniform ivec3 lightPoint; uniform mat4 lightViewProjection[MAX_MAPS]; uniform uint lightViewProjectionCount; -const vec3 e1 = vec3(0, 0, 0), e2 = vec3(1, 1, 1); +float +getShadow(vec3 positionInLightSpace, float m, vec2 texelSize) +{ + float shadow = 0.0; + for (float x = -texelSize.x; x <= texelSize.x; x += texelSize.x) { + for (float y = -texelSize.y; y <= texelSize.y; y += texelSize.y) { + const float lightSpaceDepth = texture(shadowMap, vec3(positionInLightSpace.xy + vec2(x, y), m)).r; + shadow += step(positionInLightSpace.z, lightSpaceDepth + 0.001); + } + } + return shadow / 9.0; +} float -insideShadowCube(vec3 v) +insideShadowCube(vec3 v, vec2 texelSize) { - const vec3 s = step(e1, v) - step(e2, v); + const vec3 s = step(vec3(texelSize, 0), v) - step(vec3(1 - texelSize, 1), v); return s.x * s.y * s.z; } float isShaded(vec4 Position) { + const vec2 texelSize = 1.0 / textureSize(shadowMap, 0).xy; for (uint m = 0u; m < lightViewProjectionCount; m++) { - const vec3 PositionInLightSpace = (lightViewProjection[m] * Position).xyz; - const float inside = insideShadowCube(PositionInLightSpace); + const vec3 positionInLightSpace = (lightViewProjection[m] * Position).xyz; + const float inside = insideShadowCube(positionInLightSpace, texelSize); if (inside > 0) { - const float lightSpaceDepth = texture(shadowMap, vec3(PositionInLightSpace.xy, m)).r; - return step(lightSpaceDepth, PositionInLightSpace.z); + return getShadow(positionInLightSpace, m, texelSize); } } - return 0; + return 1; } void @@ -46,5 +57,5 @@ main() const vec4 Position = vec4(texture(gPosition, TexCoords).xyz - lightPoint, 1); const vec3 Normal = texture(gNormal, TexCoords).rgb; const float shaded = isShaded(Position); - FragColor = (1 - shaded) * max(dot(-lightDirection, Normal) * lightColour, 0); + FragColor = shaded * max(dot(-lightDirection, Normal) * lightColour, 0); } diff --git a/gfx/gl/shaders/dynamicPoint.vs b/gfx/gl/shaders/dynamicPoint.vs index 7551688..97d2983 100644 --- a/gfx/gl/shaders/dynamicPoint.vs +++ b/gfx/gl/shaders/dynamicPoint.vs @@ -1,6 +1,8 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable +layout(binding = 1) uniform usampler2DRect materialData; + include(`meshIn.glsl') include(`materialInterface.glsl') diff --git a/gfx/gl/shaders/dynamicPointInst.vs b/gfx/gl/shaders/dynamicPointInst.vs index 69eab0c..a85f9c9 100644 --- a/gfx/gl/shaders/dynamicPointInst.vs +++ b/gfx/gl/shaders/dynamicPointInst.vs @@ -1,6 +1,8 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable +layout(binding = 1) uniform usampler2DRect materialData; + include(`meshIn.glsl') include(`materialInterface.glsl') diff --git a/gfx/gl/shaders/fixedPoint.vs b/gfx/gl/shaders/fixedPoint.vs index 5cfe9b3..435b3d1 100644 --- a/gfx/gl/shaders/fixedPoint.vs +++ b/gfx/gl/shaders/fixedPoint.vs @@ -1,6 +1,8 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable +layout(binding = 1) uniform usampler2DRect materialData; + include(`meshIn.glsl') include(`materialInterface.glsl') diff --git a/gfx/gl/shaders/getMaterialDetail.glsl b/gfx/gl/shaders/getMaterialDetail.glsl new file mode 100644 index 0000000..f819fb2 --- /dev/null +++ b/gfx/gl/shaders/getMaterialDetail.glsl @@ -0,0 +1,12 @@ +MaterialDetail +getMaterialDetail(uint midx) +{ + if (midx > 0u) { + const vec4 sPosSize = texture(materialData, uvec2(0, midx - 1u)); + const uvec4 sMode = texture(materialData, uvec2(1, midx - 1u)); + const uint mapmodeU = sMode.x & 0xFu; + const uint mapmodeV = (sMode.x & 0xF0u) >> 1; + return MaterialDetail(sPosSize.xy, sPosSize.zw, uvec2(mapmodeU, mapmodeV)); + } + return MaterialDetail(vec2(0, 0), vec2(0, 0), uvec2(0, 0)); +} diff --git a/gfx/gl/shaders/material.fs b/gfx/gl/shaders/material.fs index 5b93707..37f2e27 100644 --- a/gfx/gl/shaders/material.fs +++ b/gfx/gl/shaders/material.fs @@ -1,41 +1,11 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable +layout(binding = 0) uniform sampler2D textureAlbedo; + include(`materialInterface.glsl') include(`materialOut.glsl') - -layout(binding = 0) uniform sampler2D texture0; - -float map(uint mapmode, float value) -{ - switch (mapmode) { - case 0u: // Repeat - return fract(value); - case 1u: // Clamp to edge - return clamp(0.0, 1.0, value); - case 2u: // Mirror - discard; - case 3u: // Decal - if (value != clamp(0.0, 1.0, value)) { - discard; - } - } - return 0; -} - -vec2 map(uvec2 mapmode, vec2 value) -{ - return vec2(map(mapmode.x, value.x), map(mapmode.y, value.y)); -} - -vec4 getTextureColour(MaterialDetail mat, vec2 uv) -{ - if (mat.textureSize.x > 0) { - const vec2 tSize = textureSize(texture0, 0); - uv = (mat.textureOrigin + mat.textureSize * map(mat.mapmode, uv)) / tSize; - } - return texture(texture0, uv); -} +include(`materialCommon.glsl') void main() diff --git a/gfx/gl/shaders/materialCommon.glsl b/gfx/gl/shaders/materialCommon.glsl new file mode 100644 index 0000000..3fe2237 --- /dev/null +++ b/gfx/gl/shaders/materialCommon.glsl @@ -0,0 +1,33 @@ +float +map(uint mapmode, float value) +{ + switch (mapmode) { + case 0u: // Repeat + return fract(value); + case 1u: // Clamp to edge + return clamp(0.0, 1.0, value); + case 2u: // Mirror + discard; + case 3u: // Decal + if (value != clamp(0.0, 1.0, value)) { + discard; + } + } + return 0; +} + +vec2 +map(uvec2 mapmode, vec2 value) +{ + return vec2(map(mapmode.x, value.x), map(mapmode.y, value.y)); +} + +vec4 +getTextureColour(MaterialDetail mat, vec2 uv) +{ + if (mat.textureSize.x > 0) { + const vec2 tSize = textureSize(textureAlbedo, 0); + uv = (mat.textureOrigin + mat.textureSize * map(mat.mapmode, uv)) / tSize; + } + return texture(textureAlbedo, uv); +} diff --git a/gfx/gl/shaders/materialDetail.glsl b/gfx/gl/shaders/materialDetail.glsl new file mode 100644 index 0000000..a208d50 --- /dev/null +++ b/gfx/gl/shaders/materialDetail.glsl @@ -0,0 +1,5 @@ +struct MaterialDetail { + vec2 textureOrigin; + vec2 textureSize; + uvec2 mapmode; +}; diff --git a/gfx/gl/shaders/materialInterface.glsl b/gfx/gl/shaders/materialInterface.glsl index 3a4796b..f2ca297 100644 --- a/gfx/gl/shaders/materialInterface.glsl +++ b/gfx/gl/shaders/materialInterface.glsl @@ -1,13 +1,9 @@ -struct MaterialDetail { - vec2 textureOrigin; - vec2 textureSize; - uvec2 mapmode; -}; +include(`materialDetail.glsl') -ifelse(TYPE, .fs, in, out) vec3 FragPos; -ifelse(TYPE, .fs, in, out) vec2 TexCoords; -ifelse(TYPE, .fs, in, out) vec3 Normal; -ifelse(TYPE, .fs, in, out) vec4 Colour; -flat -ifelse(TYPE, .fs, in, out) -MaterialDetail Material; +define(INOUT, ifelse(TYPE, .fs, in, out)); + +INOUT vec3 FragPos; +INOUT vec2 TexCoords; +INOUT vec3 Normal; +INOUT vec4 Colour; +flat INOUT MaterialDetail Material; diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs index 90519e3..47ce9c0 100644 --- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs +++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs @@ -1,14 +1,18 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable -layout(binding = 3) uniform sampler2D texture0; +layout(binding = 3) uniform sampler2D textureAlbedo; + +include(`materialInterface.glsl') +include(`materialCommon.glsl') in vec2 texCoord; +flat in MaterialDetail material; void main() { - if (texture(texture0, texCoord).a < 0.5) { + if (getTextureColour(material, texCoord).a < 0.5) { discard; } gl_FragDepth = gl_FragCoord.z; diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs index 27ad9d7..a76c87f 100644 --- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs +++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs @@ -1,3 +1,15 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +layout(binding = 4) uniform usampler2DRect materialData; + define(`TEXTURES', 1) +include(`materialInterface.glsl') +include(`getMaterialDetail.glsl') +include(`meshIn.glsl') + +uniform ivec3 viewPoint; +layout(location = 5) in mat3 model; +layout(location = 8) in ivec3 modelPos; -include(`shadowDynamicPointInst.vs') +include(`commonShadowPoint.glsl') 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; +}; |