diff options
Diffstat (limited to 'gfx')
87 files changed, 1338 insertions, 599 deletions
diff --git a/gfx/aabb.h b/gfx/aabb.h new file mode 100644 index 0000000..ce15a0f --- /dev/null +++ b/gfx/aabb.h @@ -0,0 +1,41 @@ +#pragma once + +#include "maths.h" +#include <algorithm> +#include <tuple> + +template<Arithmetic T, glm::qualifier Q = glm::defaultp> class AxisAlignedBoundingBox { +public: + using V = glm::vec<3, T, Q>; + AxisAlignedBoundingBox() = default; + + AxisAlignedBoundingBox(const V & min, const V & max) : min {min}, max {max} { } + + AxisAlignedBoundingBox & + operator+=(const V & point) + { + min = glm::min(min, point); + max = glm::max(max, point); + return *this; + } + + AxisAlignedBoundingBox + operator-(const V & viewPoint) const + { + return {min - viewPoint, max - viewPoint}; + } + + [[nodiscard]] static AxisAlignedBoundingBox + fromPoints(auto && points) + { + using Limits = std::numeric_limits<T>; + static constexpr const auto INITIAL = std::make_pair(V {Limits::max()}, V {Limits::min()}); + return std::make_from_tuple<AxisAlignedBoundingBox<T, Q>>( + std::ranges::fold_left(points, INITIAL, [](const auto & prev, const auto & point) { + auto & [min, max] = prev; + return std::make_pair(glm::min(min, point), glm::max(max, point)); + })); + } + + V min {}, max {}; +}; diff --git a/gfx/gl/camera.cpp b/gfx/camera.cpp index 82b11a6..3bb785d 100644 --- a/gfx/gl/camera.cpp +++ b/gfx/camera.cpp @@ -4,28 +4,39 @@ #include <maths.h> #include <ray.h> -Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, GlobalDistance zNear, GlobalDistance zFar) : - position {pos}, forward {::north}, up {::up}, near {zNear}, far {zFar}, - projection { - glm::perspective(fov, aspect, static_cast<RelativeDistance>(zNear), static_cast<RelativeDistance>(zFar))}, - viewProjection {}, inverseViewProjection {} +Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, GlobalDistance near, GlobalDistance far) : + Camera {pos, fov, aspect, near, far, glm::lookAt({}, ::north, ::up), + glm::perspective(fov, aspect, static_cast<RelativeDistance>(near), static_cast<RelativeDistance>(far))} { - updateView(); +} + +Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, GlobalDistance near, GlobalDistance far, + const glm::mat4 & view, const glm::mat4 & projection) : + Frustum {pos, view, projection}, fov {fov}, aspect {aspect}, forward {::north}, up {::up}, near {near}, far {far} +{ +} + +void +Camera::setAspect(Angle aspect) +{ + projection = glm::perspective(fov, aspect, static_cast<RelativeDistance>(near), static_cast<RelativeDistance>(far)); + Frustum::updateCache(); } Ray<GlobalPosition3D> Camera::unProject(const ScreenRelCoord & mouse) const { - static constexpr const glm::vec4 screen {0, 0, 1, 1}; - const auto mouseProjection = glm::lookAt({}, forward, up); - return {position, glm::normalize(glm::unProject(mouse || 1.F, mouseProjection, projection, screen))}; + static constexpr const glm::vec4 SCREEN {0, 0, 1, 1}; + return { + .start = position, + .direction = glm::normalize(glm::unProject(mouse || 1.F, view, projection, SCREEN)), + }; } void Camera::updateView() { - viewProjection = projection * glm::lookAt({}, forward, up); - inverseViewProjection = glm::inverse(viewProjection); + Frustum::updateView(glm::lookAt({}, forward, up)); } Direction3D diff --git a/gfx/gl/camera.h b/gfx/camera.h index 8d53261..b17bcbb 100644 --- a/gfx/gl/camera.h +++ b/gfx/camera.h @@ -1,22 +1,19 @@ #pragma once #include "config/types.h" +#include "frustum.h" #include <glm/glm.hpp> #include <maths.h> #include <ray.h> -class Camera { +class Camera : public Frustum { public: - Camera(GlobalPosition3D, Angle fov, Angle aspect, GlobalDistance zNear, GlobalDistance zFar); - - [[nodiscard]] glm::mat4 - getViewProjection() const - { - return viewProjection; - } + Camera(GlobalPosition3D position, Angle fov, Angle aspect, GlobalDistance near, GlobalDistance far); [[nodiscard]] Ray<GlobalPosition3D> unProject(const ScreenRelCoord &) const; + void setAspect(Angle aspect); + void setPosition(const GlobalPosition3D & p) { @@ -64,24 +61,17 @@ public: return forward; } - [[nodiscard]] auto - getPosition() const - { - return position; - } - [[nodiscard]] std::array<GlobalPosition4D, 4> extentsAtDist(GlobalDistance) const; [[nodiscard]] static Direction3D upFromForward(const Direction3D & forward); private: + Camera(GlobalPosition3D position, Angle fov, Angle aspect, GlobalDistance near, GlobalDistance far, + const glm::mat4 & view, const glm::mat4 & projection); void updateView(); - GlobalPosition3D position; + Angle fov, aspect; Direction3D forward; Direction3D up; - GlobalDistance near, far; - glm::mat4 projection; - glm::mat4 viewProjection, inverseViewProjection; }; diff --git a/gfx/followCameraController.cpp b/gfx/followCameraController.cpp index 52dfb35..d7bbc0b 100644 --- a/gfx/followCameraController.cpp +++ b/gfx/followCameraController.cpp @@ -1,6 +1,6 @@ #include "followCameraController.h" #include "game/vehicles/vehicle.h" -#include <gfx/gl/camera.h> +#include <gfx/camera.h> #include <glm/glm.hpp> #include <location.h> #include <maths.h> @@ -24,7 +24,7 @@ FollowCameraController::updateCamera(Camera * camera) const break; case Mode::Ride: - camera->setView(pos + (up * 4.8F), -sincosf(rot.y) || 0.F); + camera->setView(pos + (up * 4.8F), -sincos(rot.y) || 0.F); break; case Mode::ISO: diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp new file mode 100644 index 0000000..faa676d --- /dev/null +++ b/gfx/frustum.cpp @@ -0,0 +1,67 @@ +#include "frustum.h" +#include <algorithm> +#include <collections.h> +#include <glm/ext/matrix_transform.hpp> + +static constexpr auto PLANES = std::array {0, 1, 2} * std::array {-1.F, 1.F}; + +Frustum::Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection) : + position {pos}, view {view}, projection {projection}, viewProjection {}, inverseViewProjection {}, planes {} +{ + updateCache(); +} + +void +Frustum::updateView(const glm::mat4 & newView) +{ + view = newView; + updateCache(); +} + +bool +Frustum::contains(const BoundingBox & aabb) const +{ + return boundByPlanes(aabb, FACES); +} + +bool +Frustum::shadedBy(const BoundingBox & aabb) const +{ + return boundByPlanes(aabb, FACES - 1); +} + +bool +Frustum::boundByPlanes(const BoundingBox & aabb, size_t nplanes) const +{ + static constexpr auto EXTENT_CORNER_IDXS = [] { + using Extent = GlobalPosition3D BoundingBox::*; + constexpr auto EXTENTS = std::array {&BoundingBox::min, &BoundingBox::max}; + std::array<glm::vec<3, Extent>, 2ZU * 2ZU * 2ZU> out {}; + std::ranges::copy(std::views::cartesian_product(EXTENTS, EXTENTS, EXTENTS) + | std::views::transform( + std::make_from_tuple<glm::vec<3, Extent>, std::tuple<Extent, Extent, Extent>>), + out.begin()); + return out; + }(); + + const std::array<RelativePosition4D, 8> corners + = EXTENT_CORNER_IDXS * [relativeAabb = aabb - position](auto idxs) -> glm::vec4 { + return {(relativeAabb.*(idxs.x)).x, (relativeAabb.*(idxs.y)).y, (relativeAabb.*(idxs.z)).z, 1.F}; + }; + return std::ranges::none_of(std::span(planes).subspan(0, nplanes), [&corners](const auto & frustumPlane) { + return (std::ranges::all_of(corners, [&frustumPlane](const auto & corner) { + return glm::dot(frustumPlane, corner) < 0.F; + })); + }); +} + +void +Frustum::updateCache() +{ + viewProjection = projection * view; + inverseViewProjection = glm::inverse(viewProjection); + std::ranges::transform(PLANES, planes.begin(), [vpt = glm::transpose(viewProjection)](const auto & idxs) { + const auto [idx, sgn] = idxs; + return vpt[3] + (vpt[idx] * sgn); + }); +} diff --git a/gfx/frustum.h b/gfx/frustum.h new file mode 100644 index 0000000..a2d90e9 --- /dev/null +++ b/gfx/frustum.h @@ -0,0 +1,45 @@ +#pragma once + +#include "aabb.h" +#include "config/types.h" +#include <array> +#include <glm/mat4x4.hpp> + +class Frustum { +public: + Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection); + + [[nodiscard]] auto & + getFrustumPlanes() const + { + return planes; + } + + [[nodiscard]] auto & + getViewProjection() const + { + return viewProjection; + } + + [[nodiscard]] auto + getPosition() const + { + return position; + } + + void updateView(const glm::mat4 & view); + + using BoundingBox = AxisAlignedBoundingBox<GlobalDistance>; + [[nodiscard]] bool contains(const BoundingBox &) const; + [[nodiscard]] bool shadedBy(const BoundingBox &) const; + +protected: + static constexpr size_t FACES = 6; + void updateCache(); + [[nodiscard]] bool boundByPlanes(const BoundingBox &, size_t nplanes) const; + + GlobalPosition3D position; + glm::mat4 view, projection; + glm::mat4 viewProjection, inverseViewProjection; + std::array<glm::vec4, FACES> planes; +}; diff --git a/gfx/gl/gldebug.cpp b/gfx/gl/gldebug.cpp new file mode 100644 index 0000000..518d4fb --- /dev/null +++ b/gfx/gl/gldebug.cpp @@ -0,0 +1,20 @@ +#if GLDEBUG == 2 +// Level 2 is out of line because its "complex" + +# include "gldebug.h" +# include <format> + +glDebugScope::glDebugScope(GLuint id, const std::source_location & location) +{ + const auto fullMsg = std::format("{} ({}:{})", location.function_name(), location.file_name(), location.line()); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(fullMsg.length()), fullMsg.c_str()); +} + +glDebugScope::glDebugScope(GLuint id, const std::string_view msg, const std::source_location & location) +{ + const auto fullMsg + = std::format("{} @ {} ({}:{})", msg, location.function_name(), location.file_name(), location.line()); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(fullMsg.length()), fullMsg.c_str()); +} + +#endif diff --git a/gfx/gl/gldebug.h b/gfx/gl/gldebug.h new file mode 100644 index 0000000..5cfd099 --- /dev/null +++ b/gfx/gl/gldebug.h @@ -0,0 +1,52 @@ +#pragma once +#ifndef GLDEBUG +# define GLDEBUG 0 +#endif + +#include "special_members.h" +#include <glad/gl.h> +#include <source_location> +#include <string_view> + +class [[nodiscard]] glDebugScope { +public: + explicit glDebugScope(GLuint id, const std::source_location & = std::source_location::current()); + explicit glDebugScope( + GLuint id, std::string_view msg, const std::source_location & = std::source_location::current()); + + ~glDebugScope(); + + constexpr + operator bool() const + { + return true; + } + + NO_MOVE(glDebugScope); + NO_COPY(glDebugScope); +}; + +#if GLDEBUG > 0 +inline glDebugScope::~glDebugScope() +{ + glPopDebugGroup(); +} +# if GLDEBUG == 1 +// Level 1 is inlined for performance because they're thin wrappers +inline glDebugScope::glDebugScope(GLuint id, const std::source_location & location) : + glDebugScope {id, location.function_name()} +{ +} + +inline glDebugScope::glDebugScope(GLuint id, const std::string_view msg, const std::source_location &) +{ + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(msg.length()), msg.data()); +} +# endif +#else +inline glDebugScope::glDebugScope(GLuint, const std::source_location &) { } + +inline glDebugScope::glDebugScope(GLuint, const std::string_view, const std::source_location &) { } + +inline glDebugScope::~glDebugScope() = default; +#endif diff --git a/gfx/gl/program.cpp b/gfx/gl/program.cpp index 7287fde..da85456 100644 --- a/gfx/gl/program.cpp +++ b/gfx/gl/program.cpp @@ -1,5 +1,5 @@ #include "program.h" -#include "shader.h" +#include "gldebug.h" #include <format> #include <glm/gtc/type_ptr.hpp> #include <glm/gtx/transform.hpp> @@ -9,11 +9,12 @@ void Program::linkAndValidate() const { + glDebugScope _ {m_program}; 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 +23,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..2b06d2d 100644 --- a/gfx/gl/program.h +++ b/gfx/gl/program.h @@ -1,6 +1,7 @@ #pragma once -#include "shader.h" +#include "gldebug.h" +#include "shader.h" // IWYU pragma: export #include <glRef.h> #include <glad/gl.h> #include <glm/mat4x4.hpp> @@ -12,7 +13,11 @@ using ProgramRef = glRef<GLuint, &glCreateProgram, &glDeleteProgram>; class Program { public: - template<typename... S> explicit Program(const S &... srcs) + Program() = delete; + + template<typename... S> explicit Program(const S &... srcs) : Program {glDebugScope {0}, srcs...} { } + + template<typename... S> explicit Program(glDebugScope, const S &... srcs) { (glAttachShader(m_program, srcs.compile()), ...); linkAndValidate(); @@ -31,6 +36,12 @@ public: return location; } + explicit + operator bool() const + { + return location >= 0; + } + protected: GLint location; }; @@ -47,6 +58,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..3681c60 100644 --- a/gfx/gl/sceneProvider.cpp +++ b/gfx/gl/sceneProvider.cpp @@ -5,10 +5,10 @@ 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 -SceneProvider::shadows(const ShadowMapper &) const +SceneProvider::shadows(const ShadowMapper &, const Frustum &) const { } diff --git a/gfx/gl/sceneProvider.h b/gfx/gl/sceneProvider.h index f5e8e99..f6b7009 100644 --- a/gfx/gl/sceneProvider.h +++ b/gfx/gl/sceneProvider.h @@ -5,6 +5,7 @@ class SceneRenderer; class ShadowMapper; class SceneShader; +class Frustum; class SceneProvider { public: @@ -12,8 +13,8 @@ public: virtual ~SceneProvider() = default; DEFAULT_MOVE_COPY(SceneProvider); - virtual void content(const SceneShader &) const = 0; + virtual void content(const SceneShader &, const Frustum &) const = 0; virtual void environment(const SceneShader &, const SceneRenderer &) const; virtual void lights(const SceneShader &) const = 0; - virtual void shadows(const ShadowMapper &) const; + virtual void shadows(const ShadowMapper &, const Frustum &) const; }; diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index e0938f2..5e8241f 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -1,9 +1,9 @@ #include "sceneRenderer.h" #include "maths.h" #include "vertexArrayObject.h" -#include <gfx/gl/shaders/fs-directionalLight.h> -#include <gfx/gl/shaders/fs-lighting.h> -#include <gfx/gl/shaders/vs-lighting.h> +#include <gfx/gl/shaders/directionalLight-frag.h> +#include <gfx/gl/shaders/lighting-frag.h> +#include <gfx/gl/shaders/lighting-vert.h> #include <glm/gtc/type_ptr.hpp> static constexpr const std::array<const glm::i8vec4, 4> displayVAOdata {{ @@ -14,9 +14,11 @@ static constexpr const std::array<const glm::i8vec4, 4> displayVAOdata {{ {1, -1, 1, 0}, }}; -SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) : +SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) : SceneRenderer {s, o, glDebugScope {o}} { } + +SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o, glDebugScope) : camera {{-1250000, -1250000, 35.0F}, quarter_pi, ratio(s), 100, 10000000}, size {s}, output {o}, - lighting {lighting_vs, lighting_fs}, shadowMapper {{2048, 2048}} + lighting {lighting_vert, lighting_frag}, shadowMapper {{2048, 2048}} { shader.setViewPort({0, 0, size.x, size.y}); VertexArrayObject {displayVAO}.addAttribs<glm::i8vec4>(displayVBO, displayVAOdata); @@ -39,7 +41,7 @@ SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) : glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); configuregdata(gPosition, {GL_RGB32I}, GL_RGB_INTEGER, GL_COLOR_ATTACHMENT0); - configuregdata(gNormal, {GL_RGB8_SNORM, GL_RGB16F}, GL_RGB, GL_COLOR_ATTACHMENT1); + normaliFormat = configuregdata(gNormal, {GL_RGB8_SNORM, GL_RGB16F}, GL_RGB, GL_COLOR_ATTACHMENT1); configuregdata(gAlbedoSpec, {GL_RGB8}, GL_RGB, GL_COLOR_ATTACHMENT2); constexpr std::array<unsigned int, 3> attachments { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2}; @@ -57,66 +59,110 @@ SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) : } void +SceneRenderer::resize(ScreenAbsCoord newSize) +{ + glDebugScope _ {output}; + size = newSize; + camera.setAspect(ratio(size)); + const auto configuregdata = [this](const GLuint data, const GLint iformat, const GLenum format) { + glBindTexture(GL_TEXTURE_2D, data); + glTexImage2D(GL_TEXTURE_2D, 0, iformat, size.x, size.y, 0, format, GL_BYTE, nullptr); + }; + configuregdata(gPosition, GL_RGB32I, GL_RGB_INTEGER); + configuregdata(gNormal, normaliFormat, GL_RGB); + configuregdata(gAlbedoSpec, GL_RGB8, GL_RGB); + configuregdata(gIllumination, GL_RGB8, GL_RGB); + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.x, size.y); + shader.setViewPort({0, 0, size.x, size.y}); +} + +void SceneRenderer::render(const SceneProvider & scene) const { + glDebugScope _ {output}; shader.setViewProjection(camera.getPosition(), camera.getViewProjection()); glViewport(0, 0, size.x, size.y); - // Geometry pass - glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); - glEnable(GL_BLEND); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_DEPTH_TEST); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - scene.content(shader); - - // Illumination pass - 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); - scene.environment(shader, *this); - glDisable(GL_DEPTH_TEST); - scene.lights(shader); - - // Lighting pass - glBindFramebuffer(GL_FRAMEBUFFER, output); - glViewport(0, 0, size.x, size.y); - glCullFace(GL_BACK); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, gIllumination); - lighting.use(); - renderQuad(); + if (glDebugScope _ {gBuffer, "Geometry/colour pass"}) { + // Geometry/colour pass - writes albedo, normal and position textures + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + glEnable(GL_BLEND); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + scene.content(shader, camera); + } + + if (glDebugScope _ {gBufferIll, "Environment pass"}) { + // Environment pass - + // * ambient - clears illumination texture - see setAmbientLight + // * directional - updates shadowMapper, reads normal and position, writes illumination - see + // setDirectionalLight + scene.environment(shader, *this); + } + + if (glDebugScope _ {gBufferIll, "Scene lighting pass"}) { + // Scene lights pass - + // * per light - reads normal and position, writes illumination + 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); + glDisable(GL_DEPTH_TEST); + scene.lights(shader); + } + + if (glDebugScope _ {output, "Composition pass"}) { + // Composition pass - reads albedo and illumination, writes output + glBindFramebuffer(GL_FRAMEBUFFER, output); + glViewport(0, 0, size.x, size.y); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, gIllumination); + lighting.use(); + renderQuad(); + } } void SceneRenderer::setAmbientLight(const RGB & colour) const { + glDebugScope _ {output}; glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll); glClearColor(colour.r, colour.g, colour.b, 1.0F); glClear(GL_COLOR_BUFFER_BIT); } 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) { + glDebugScope _ {output}; 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(); } } @@ -129,7 +175,7 @@ SceneRenderer::renderQuad() const glBindVertexArray(0); } -SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() : Program {lighting_vs, directionalLight_fs} { } +SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() : Program {lighting_vert, directionalLight_frag} { } const auto toTextureSpaceMat = glm::translate(glm::identity<glm::mat4>(), glm::vec3 {0.5F}) * glm::scale(glm::identity<glm::mat4>(), glm::vec3 {0.5F}); @@ -142,8 +188,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..05921a1 100644 --- a/gfx/gl/sceneRenderer.h +++ b/gfx/gl/sceneRenderer.h @@ -1,20 +1,25 @@ #pragma once -#include "camera.h" +#include "gfx/lightDirection.h" #include "glArrays.h" +#include "gldebug.h" #include "program.h" #include "sceneProvider.h" #include "sceneShader.h" #include "shadowMapper.h" +#include <gfx/camera.h> #include <glm/fwd.hpp> class SceneRenderer { public: - explicit SceneRenderer(ScreenAbsCoord size, GLuint output); + SceneRenderer(ScreenAbsCoord size, GLuint output); + SceneRenderer(ScreenAbsCoord size, GLuint output, glDebugScope); + + void resize(ScreenAbsCoord size); 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; @@ -24,6 +29,7 @@ protected: ScreenAbsCoord size; GLuint output; glFrameBuffer gBuffer, gBufferIll; + GLint normaliFormat; glTexture gPosition, gNormal, gAlbedoSpec, gIllumination; glRenderBuffer depth; diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp index 4cbccb3..72cde91 100644 --- a/gfx/gl/sceneShader.cpp +++ b/gfx/gl/sceneShader.cpp @@ -1,23 +1,25 @@ #include "sceneShader.h" -#include <gfx/gl/shaders/fs-landmass.h> -#include <gfx/gl/shaders/fs-material.h> -#include <gfx/gl/shaders/fs-network.h> -#include <gfx/gl/shaders/fs-pointLight.h> -#include <gfx/gl/shaders/fs-spotLight.h> -#include <gfx/gl/shaders/fs-water.h> -#include <gfx/gl/shaders/gs-networkCurve.h> -#include <gfx/gl/shaders/gs-networkStraight.h> -#include <gfx/gl/shaders/gs-pointLight.h> -#include <gfx/gl/shaders/gs-spotLight.h> -#include <gfx/gl/shaders/vs-dynamicPoint.h> -#include <gfx/gl/shaders/vs-dynamicPointInst.h> -#include <gfx/gl/shaders/vs-fixedPoint.h> -#include <gfx/gl/shaders/vs-landmass.h> -#include <gfx/gl/shaders/vs-networkCurve.h> -#include <gfx/gl/shaders/vs-networkStraight.h> -#include <gfx/gl/shaders/vs-pointLight.h> -#include <gfx/gl/shaders/vs-spotLight.h> -#include <gfx/gl/shaders/vs-water.h> +#include <gfx/gl/shaders/dynamicPoint-vert.h> +#include <gfx/gl/shaders/dynamicPointInst-vert.h> +#include <gfx/gl/shaders/fixedPoint-vert.h> +#include <gfx/gl/shaders/landmass-frag.h> +#include <gfx/gl/shaders/landmass-vert.h> +#include <gfx/gl/shaders/material-frag.h> +#include <gfx/gl/shaders/network-frag.h> +#include <gfx/gl/shaders/networkCurve-geom.h> +#include <gfx/gl/shaders/networkCurve-tesc.h> +#include <gfx/gl/shaders/networkCurve-tese.h> +#include <gfx/gl/shaders/networkCurve-vert.h> +#include <gfx/gl/shaders/networkStraight-geom.h> +#include <gfx/gl/shaders/networkStraight-vert.h> +#include <gfx/gl/shaders/pointLight-frag.h> +#include <gfx/gl/shaders/pointLight-geom.h> +#include <gfx/gl/shaders/pointLight-vert.h> +#include <gfx/gl/shaders/spotLight-frag.h> +#include <gfx/gl/shaders/spotLight-geom.h> +#include <gfx/gl/shaders/spotLight-vert.h> +#include <gfx/gl/shaders/water-frag.h> +#include <gfx/gl/shaders/water-vert.h> #include <gfx/gl/vertexArrayObject.h> #include <glm/gtc/type_ptr.hpp> #include <glm/gtx/transform.hpp> @@ -34,11 +36,11 @@ SceneShader::allPrograms(auto member, auto &&... ps) const } SceneShader::SceneShader() : - basicInst {dynamicPointInst_vs, material_fs}, landmass {landmass_vs, landmass_fs}, - absolute {fixedPoint_vs, material_fs}, spotLightInst {spotLight_vs, spotLight_gs, spotLight_fs}, - pointLightInst {pointLight_vs, pointLight_gs, pointLight_fs}, - networkStraight {networkStraight_vs, networkStraight_gs, network_fs}, - networkCurve {networkCurve_vs, networkCurve_gs, network_fs} + basicInst {dynamicPointInst_vert, material_frag}, absolute {fixedPoint_vert, material_frag}, + spotLightInst {spotLight_vert, spotLight_geom, spotLight_frag}, + pointLightInst {pointLight_vert, pointLight_geom, pointLight_frag}, landmass {landmass_vert, landmass_frag}, + networkStraight {networkStraight_vert, networkStraight_geom, network_frag}, + networkCurve {networkCurve_vert, networkCurve_tesc, networkCurve_tese, networkCurve_geom, network_frag} { } @@ -65,13 +67,13 @@ SceneShader::SceneProgram::setViewProjection(const GlobalPosition3D & viewPoint, void SceneShader::SceneProgram::setViewPort(const ViewPort & viewPort) const { - if (viewPortLoc >= 0) { + if (viewPortLoc) { glUseProgram(*this); glUniform(viewPortLoc, viewPort); } } -SceneShader::BasicProgram::BasicProgram() : SceneProgram {dynamicPoint_vs, material_fs} { } +SceneShader::BasicProgram::BasicProgram() : SceneProgram {dynamicPoint_vert, material_frag} { } void SceneShader::BasicProgram::setModel(Location const & location) const @@ -88,6 +90,13 @@ SceneShader::BasicProgram::use(Location const & location) const } void +SceneShader::LandmassProgram::use(const glm::vec3 colourBias) const +{ + Program::use(); + glUniform(colourBiasLos, colourBias); +} + +void SceneShader::NetworkProgram::use( const std::span<const glm::vec3> profile, const std::span<const float> texturePos) const { @@ -97,7 +106,7 @@ SceneShader::NetworkProgram::use( glUniform(profileLengthLoc, static_cast<GLuint>(profile.size())); } -SceneShader::WaterProgram::WaterProgram() : SceneProgram {water_vs, water_fs} { } +SceneShader::WaterProgram::WaterProgram() : SceneProgram {water_vert, water_frag} { } void SceneShader::WaterProgram::use(float waveCycle) const diff --git a/gfx/gl/sceneShader.h b/gfx/gl/sceneShader.h index 51f0e21..47d4397 100644 --- a/gfx/gl/sceneShader.h +++ b/gfx/gl/sceneShader.h @@ -50,6 +50,15 @@ class SceneShader { RequiredUniformLocation profileLengthLoc {*this, "profileLength"}; }; + class LandmassProgram : public AbsolutePosProgram { + public: + using AbsolutePosProgram::AbsolutePosProgram; + void use(const glm::vec3) const; + + private: + RequiredUniformLocation colourBiasLos {*this, "colourBias"}; + }; + class WaterProgram : public SceneProgram { public: WaterProgram(); @@ -64,7 +73,8 @@ public: BasicProgram basic; WaterProgram water; - AbsolutePosProgram basicInst, landmass, absolute, spotLightInst, pointLightInst; + AbsolutePosProgram basicInst, absolute, spotLightInst, pointLightInst; + LandmassProgram landmass; NetworkProgram networkStraight, networkCurve; void setViewProjection(const GlobalPosition3D & viewPoint, const glm::mat4 & viewProjection) const; diff --git a/gfx/gl/shader.cpp b/gfx/gl/shader.cpp index 0bc127a..319726f 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> @@ -7,17 +8,51 @@ namespace { auto - getInt(GLenum e) + getInt(GLenum pname) { - GLint i {}; - glGetIntegerv(e, &i); - return std::to_string(i); + GLint data {}; + glGetIntegerv(pname, &data); + return std::to_string(data); } using LookUpFunction = std::string (*)(GLenum); - constexpr std::array<std::tuple<std::string_view, GLenum, LookUpFunction>, 1> LOOKUPS {{ + constexpr auto LOOKUPS = std::to_array<std::tuple<std::string_view, GLenum, LookUpFunction>>({ {"GL_MAX_GEOMETRY_OUTPUT_VERTICES", GL_MAX_GEOMETRY_OUTPUT_VERTICES, getInt}, - }}; + {"GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS", GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, 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 +64,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(), + while (const auto match = ctre::search<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; }); @@ -38,6 +73,9 @@ Shader::compile() const const auto & [name, pname, getFunction] = *lookup; textMod.replace(match.begin(), match.end(), getFunction(pname)); } + else { + throw std::domain_error(std::format("Unknown shader constant: {}", match.view())); + } } source(textMod.c_str(), static_cast<GLint>(textMod.length())); } @@ -46,31 +84,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); - } - else { - glGetShaderiv(shader, flag, &success); + if (getShaderParam(shader, GL_COMPILE_STATUS) == GL_FALSE) { + throw ShaderCompileError {shader, text}; } +} - 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.gs b/gfx/gl/shaders/commonShadowPoint.geom index b99bd20..a515d97 100644 --- a/gfx/gl/shaders/commonShadowPoint.gs +++ b/gfx/gl/shaders/commonShadowPoint.geom @@ -1,5 +1,6 @@ -#version 330 core -#extension GL_ARB_viewport_array : enable +#version 460 core + +ifdef(`TEXTURES', include(`materialDetail.glsl')) uniform mat4 viewProjection[4]; uniform int viewProjections; @@ -7,7 +8,8 @@ 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 +19,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/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/directionalLight.frag b/gfx/gl/shaders/directionalLight.frag new file mode 100644 index 0000000..e5ebfb0 --- /dev/null +++ b/gfx/gl/shaders/directionalLight.frag @@ -0,0 +1,60 @@ +#version 460 core + +const int MAX_MAPS = 4; + +out vec3 FragColor; + +in vec2 TexCoords; + +layout(binding = 0) uniform isampler2D gPosition; +layout(binding = 1) uniform sampler2D gNormal; +layout(binding = 2) uniform sampler2DArray shadowMap; + +uniform vec3 lightDirection; +uniform vec3 lightColour; +uniform ivec3 lightPoint; +uniform mat4 lightViewProjection[MAX_MAPS]; +uniform uint lightViewProjectionCount; + +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, vec2 texelSize) +{ + 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, texelSize); + if (inside > 0) { + return getShadow(positionInLightSpace, m, texelSize); + } + } + return 1.0; +} + +void +main() +{ + const vec4 Position = vec4(texture(gPosition, TexCoords).xyz - lightPoint, 1); + const vec3 Normal = texture(gNormal, TexCoords).rgb; + const float shaded = isShaded(Position); + FragColor = shaded * max(dot(-lightDirection, Normal) * lightColour, 0); +} diff --git a/gfx/gl/shaders/directionalLight.fs b/gfx/gl/shaders/directionalLight.fs deleted file mode 100644 index 24457b8..0000000 --- a/gfx/gl/shaders/directionalLight.fs +++ /dev/null @@ -1,50 +0,0 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable - -const int MAX_MAPS = 4; - -out vec3 FragColor; - -in vec2 TexCoords; - -layout(binding = 0) uniform isampler2D gPosition; -layout(binding = 1) uniform sampler2D gNormal; -layout(binding = 2) uniform sampler2DArray shadowMap; - -uniform vec3 lightDirection; -uniform vec3 lightColour; -uniform ivec3 lightPoint; -uniform mat4 lightViewProjection[MAX_MAPS]; -uniform uint lightViewProjectionCount; - -const vec3 e1 = vec3(0, 0, 0), e2 = vec3(1, 1, 1); - -float -insideShadowCube(vec3 v) -{ - const vec3 s = step(e1, v) - step(e2, v); - return s.x * s.y * s.z; -} - -float -isShaded(vec4 Position) -{ - for (uint m = 0u; m < lightViewProjectionCount; m++) { - const vec3 PositionInLightSpace = (lightViewProjection[m] * Position).xyz; - const float inside = insideShadowCube(PositionInLightSpace); - if (inside > 0) { - const float lightSpaceDepth = texture(shadowMap, vec3(PositionInLightSpace.xy, m)).r; - return step(lightSpaceDepth, PositionInLightSpace.z); - } - } - return 0; -} - -void -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); -} diff --git a/gfx/gl/shaders/dynamicPoint.vs b/gfx/gl/shaders/dynamicPoint.vert index 7551688..cd5fc26 100644 --- a/gfx/gl/shaders/dynamicPoint.vs +++ b/gfx/gl/shaders/dynamicPoint.vert @@ -1,5 +1,6 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core + +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.vert index 69eab0c..46fead9 100644 --- a/gfx/gl/shaders/dynamicPointInst.vs +++ b/gfx/gl/shaders/dynamicPointInst.vert @@ -1,5 +1,6 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core + +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.vert index 5cfe9b3..9201251 100644 --- a/gfx/gl/shaders/fixedPoint.vs +++ b/gfx/gl/shaders/fixedPoint.vert @@ -1,5 +1,6 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core + +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/landmass.fs b/gfx/gl/shaders/landmass.frag index 55e3c24..b5c7fa1 100644 --- a/gfx/gl/shaders/landmass.fs +++ b/gfx/gl/shaders/landmass.frag @@ -1,12 +1,12 @@ -#version 330 core +#version 460 core include(`materialOut.glsl') in vec3 FragPos; in vec3 Normal; -flat in vec3 ColourBias; uniform sampler2D texture0; uniform ivec3 viewPoint; +uniform vec3 colourBias; const vec3 grass = vec3(.1, .4, .05); const vec3 slope = vec3(.6, .6, .4); @@ -35,8 +35,8 @@ main() vec3 color = texture(texture0, vec2(position.xy % 10000) / 10000.0).rgb; int height = position.z; - if (ColourBias.r >= 0) { - color *= ColourBias; + if (colourBias.r >= 0) { + color *= colourBias; } else if (height < beachline) { // Sandy beach color *= sand; diff --git a/gfx/gl/shaders/landmass.vs b/gfx/gl/shaders/landmass.vert index 9617cb9..91fc7f8 100644 --- a/gfx/gl/shaders/landmass.vs +++ b/gfx/gl/shaders/landmass.vert @@ -1,13 +1,10 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core layout(location = 0) in ivec3 position; layout(location = 1) in vec3 normal; -layout(location = 2) in vec3 colourBias; out vec3 FragPos; out vec3 Normal; -flat out vec3 ColourBias; uniform mat4 viewProjection; uniform ivec3 viewPoint; @@ -17,7 +14,6 @@ main() { FragPos = position - viewPoint; Normal = normal; - ColourBias = colourBias; gl_Position = viewProjection * vec4(FragPos, 1); } diff --git a/gfx/gl/shaders/lighting.fs b/gfx/gl/shaders/lighting.frag index 4646b75..b5c6c8b 100644 --- a/gfx/gl/shaders/lighting.fs +++ b/gfx/gl/shaders/lighting.frag @@ -1,5 +1,4 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core out vec3 FragColor; diff --git a/gfx/gl/shaders/lighting.vs b/gfx/gl/shaders/lighting.vert index e07cd0a..1046379 100644 --- a/gfx/gl/shaders/lighting.vs +++ b/gfx/gl/shaders/lighting.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core in ivec4 position; diff --git a/gfx/gl/shaders/material.frag b/gfx/gl/shaders/material.frag new file mode 100644 index 0000000..18a3169 --- /dev/null +++ b/gfx/gl/shaders/material.frag @@ -0,0 +1,18 @@ +#version 460 core + +layout(binding = 0) uniform sampler2D textureAlbedo; + +include(`materialInterface.glsl') +include(`materialOut.glsl') +include(`materialCommon.glsl') + +void +main() +{ + vec4 textureColour = getTextureColour(Material, TexCoords); + float opaque = step(0.5, mix(textureColour.a, 1, Colour.a)); + gPosition = ivec4(FragPos, opaque); + gNormal = vec4(Normal, opaque); + gl_FragDepth = mix(1.0, gl_FragCoord.z, opaque); + gAlbedoSpec = mix(textureColour, vec4(Colour.rgb, 1), Colour.a); +} diff --git a/gfx/gl/shaders/material.fs b/gfx/gl/shaders/material.fs deleted file mode 100644 index 5b93707..0000000 --- a/gfx/gl/shaders/material.fs +++ /dev/null @@ -1,49 +0,0 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable - -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); -} - -void -main() -{ - vec4 textureColour = getTextureColour(Material, TexCoords); - float opaque = step(0.5, mix(textureColour.a, 1, Colour.a)); - gPosition = ivec4(FragPos, opaque); - gNormal = vec4(Normal, opaque); - gl_FragDepth = mix(1.0, gl_FragCoord.z, opaque); - gAlbedoSpec = mix(textureColour, vec4(Colour.rgb, 1), Colour.a); -} diff --git a/gfx/gl/shaders/materialCommon.glsl b/gfx/gl/shaders/materialCommon.glsl new file mode 100644 index 0000000..4c2fbf8 --- /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.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..bf78290 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, .frag, in, out)) + +INOUT vec3 FragPos; +INOUT vec2 TexCoords; +INOUT vec3 Normal; +INOUT vec4 Colour; +flat INOUT MaterialDetail Material; diff --git a/gfx/gl/shaders/network.fs b/gfx/gl/shaders/network.frag index 4e347b4..2f291ea 100644 --- a/gfx/gl/shaders/network.fs +++ b/gfx/gl/shaders/network.frag @@ -1,5 +1,4 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core include(`materialOut.glsl') in vec3 rposition; diff --git a/gfx/gl/shaders/networkCommon.glsl b/gfx/gl/shaders/networkCommon.glsl index 0bc3c1c..7aa911d 100644 --- a/gfx/gl/shaders/networkCommon.glsl +++ b/gfx/gl/shaders/networkCommon.glsl @@ -12,13 +12,19 @@ out vec2 texCoord; out vec3 rposition; float +viewPointDist(const ivec3 position) +{ + return length(vec3(viewPoint - position)); +} + +float segDist(const ivec3 a, const ivec3 b) { - return min(distance(viewPoint, a), distance(viewPoint, b)); + return min(viewPointDist(a), viewPointDist(b)); } ifelse( - TYPE, .gs, + TYPE, .geom, // Begin: Geometry shader only function void doVertex(const ivec3 end, const uint v, const float texY, const mat2 rot) { ivec3 vpos = end + ivec3(rot * profile[v].xy, profile[v].z); diff --git a/gfx/gl/shaders/networkCurve.geom b/gfx/gl/shaders/networkCurve.geom new file mode 100644 index 0000000..4a5b3db --- /dev/null +++ b/gfx/gl/shaders/networkCurve.geom @@ -0,0 +1,17 @@ +#version 460 core + +layout(lines) in; +layout(triangle_strip, max_vertices = 10) out; + +flat in ivec3 pos[]; +flat in mat2 rot[]; +flat in float tpos[]; +flat in float dist[]; + +include(`networkCommon.glsl') + +void +main() +{ + doSeg(min(dist[0], dist[1]), pos[0], pos[1], tpos[0], tpos[1], rot[0], rot[1]); +} diff --git a/gfx/gl/shaders/networkCurve.gs b/gfx/gl/shaders/networkCurve.gs deleted file mode 100644 index 7cb6c42..0000000 --- a/gfx/gl/shaders/networkCurve.gs +++ /dev/null @@ -1,47 +0,0 @@ -#version 330 core - -flat in ivec3 apos[]; -flat in ivec3 bpos[]; -flat in ivec3 cpos[]; -flat in float reps[]; -flat in float aangle[]; -flat in float bangle[]; -flat in float radius[]; - -layout(points) in; -layout(triangle_strip, max_vertices = GL_MAX_GEOMETRY_OUTPUT_VERTICES) out; - -const mat2 rot = mat2(1); - -include(`networkCommon.glsl') - -mat2 -getRot(float angle) -{ - return mat2(cos(angle), sin(angle), -sin(angle), cos(angle)); -} - -void -main() -{ - float segs = clamp( - round(reps[0] * radius[0] / 1000), 4, floor(uint(GL_MAX_GEOMETRY_OUTPUT_VERTICES) / (profileLength * 2u))); - vec3 arcstep = vec3((bangle[0] - aangle[0]), // angle - reps[0], // texture - (bpos[0].z - apos[0].z)) // height - / segs; - - ivec3 prevPos = apos[0]; - mat2 prevRot = getRot(aangle[0]); - float prevTex = 0; - for (vec3 arc = arcstep; arc.y < reps[0] - 0.01; arc += arcstep) { - mat2 rot = getRot(arc.x + aangle[0]); - ivec3 pos = cpos[0] + ivec3(rot * vec2(radius[0], 0), arc.z); - float tex = arc.y; - doSeg(segDist(prevPos, pos), pos, prevPos, tex, prevTex, rot, prevRot); - prevPos = pos; - prevRot = rot; - prevTex = tex; - } - doSeg(segDist(prevPos, bpos[0]), bpos[0], prevPos, reps[0], prevTex, getRot(bangle[0]), prevRot); -} diff --git a/gfx/gl/shaders/networkCurve.tesc b/gfx/gl/shaders/networkCurve.tesc new file mode 100644 index 0000000..5a6e449 --- /dev/null +++ b/gfx/gl/shaders/networkCurve.tesc @@ -0,0 +1,37 @@ +#version 460 core + +layout(vertices = 1) out; + +flat in ivec3 pos[][2]; +flat in ivec2 cpos[]; +flat in float reps[]; +flat in float angles[][2]; +flat in float radius[]; + +flat out ivec3 c_pos[][2]; +flat out ivec2 c_cpos[]; +flat out float c_reps[]; +flat out float c_angles[][2]; +flat out float c_radius[]; + +float +segments() +{ + const float arc = angles[gl_InvocationID][0] - angles[gl_InvocationID][1]; + const float error = 100.; + const float diff = acos(1.f - (error / radius[gl_InvocationID])); + return clamp(arc / diff, arc, 180); +} + +void +main() +{ + c_pos[gl_InvocationID] = pos[gl_InvocationID]; + c_cpos[gl_InvocationID] = cpos[gl_InvocationID]; + c_reps[gl_InvocationID] = reps[gl_InvocationID]; + c_angles[gl_InvocationID] = angles[gl_InvocationID]; + c_radius[gl_InvocationID] = radius[gl_InvocationID]; + + gl_TessLevelOuter[0] = 1; + gl_TessLevelOuter[1] = segments(); +} diff --git a/gfx/gl/shaders/networkCurve.tese b/gfx/gl/shaders/networkCurve.tese new file mode 100644 index 0000000..9cc31e2 --- /dev/null +++ b/gfx/gl/shaders/networkCurve.tese @@ -0,0 +1,45 @@ +#version 460 core + +layout(isolines, equal_spacing, cw) in; + +flat in ivec3 c_pos[][2]; +flat in ivec2 c_cpos[]; +flat in float c_reps[]; +flat in float c_angles[][2]; +flat in float c_radius[]; + +flat out ivec3 pos; +flat out mat2 rot; +flat out float tpos; +flat out float dist; + +const float startTolerance = 1. / 200.; +const float endTolerance = 1. - startTolerance; + +include(`networkCommon.glsl') + +mat2 +getRot(float angle) +{ + return mat2(cos(angle), sin(angle), -sin(angle), cos(angle)); +} + +void +main() +{ + const float angle = mix(c_angles[0][1], c_angles[0][0], gl_TessCoord.x); + rot = getRot(angle); + if (gl_TessCoord.x < startTolerance) { + pos = c_pos[0][1]; + } + else if (gl_TessCoord.x > endTolerance) { + pos = c_pos[0][0]; + } + else { + const int height = int(mix(c_pos[0][1].z, c_pos[0][0].z, gl_TessCoord.x)); + pos = ivec3(c_cpos[0] + ivec2(rot * vec2(c_radius[0], 0)), height); + } + + tpos = c_reps[0] * gl_TessCoord.x; + dist = viewPointDist(pos); +} diff --git a/gfx/gl/shaders/networkCurve.vert b/gfx/gl/shaders/networkCurve.vert new file mode 100644 index 0000000..7e363d3 --- /dev/null +++ b/gfx/gl/shaders/networkCurve.vert @@ -0,0 +1,23 @@ +#version 460 core + +layout(location = 0) in ivec3 v_pos[2]; +layout(location = 2) in ivec3 v_centre; +layout(location = 3) in float v_reps; +layout(location = 4) in float v_angles[2]; +layout(location = 6) in float v_radius; + +flat out ivec3 pos[2]; +flat out ivec2 cpos; +flat out float reps; +flat out float angles[2]; +flat out float radius; + +void +main() +{ + pos = v_pos; + cpos = v_centre.xy; + reps = v_reps; + angles = v_angles; + radius = v_radius; +} diff --git a/gfx/gl/shaders/networkCurve.vs b/gfx/gl/shaders/networkCurve.vs deleted file mode 100644 index f51bb87..0000000 --- a/gfx/gl/shaders/networkCurve.vs +++ /dev/null @@ -1,29 +0,0 @@ -#version 330 core - -layout(location = 0) in ivec3 v_apos; -layout(location = 1) in ivec3 v_bpos; -layout(location = 2) in ivec3 v_centre; -layout(location = 3) in float v_reps; -layout(location = 4) in float v_aangle; -layout(location = 5) in float v_bangle; -layout(location = 6) in float v_radius; - -flat out ivec3 apos; -flat out ivec3 bpos; -flat out ivec3 cpos; -flat out float reps; -flat out float aangle; -flat out float bangle; -flat out float radius; - -void -main() -{ - apos = v_apos; - bpos = v_bpos; - cpos = v_centre; - reps = v_reps; - aangle = v_aangle; - bangle = v_bangle; - radius = v_radius; -} diff --git a/gfx/gl/shaders/networkStraight.gs b/gfx/gl/shaders/networkStraight.geom index 51df5fb..a0e57c8 100644 --- a/gfx/gl/shaders/networkStraight.gs +++ b/gfx/gl/shaders/networkStraight.geom @@ -1,17 +1,17 @@ -#version 330 core +#version 460 core -flat in ivec3 apos[]; -flat in ivec3 bpos[]; +layout(points) in; +layout(triangle_strip, max_vertices = 10) out; + +flat in ivec3 pos[][2]; flat in mat2 rot[]; flat in float reps[]; flat in float dist[]; -layout(points) in; -layout(triangle_strip, max_vertices = 10) out; include(`networkCommon.glsl') void main() { - doSeg(dist[0], apos[0], bpos[0], 0.f, reps[0], rot[0], rot[0]); + doSeg(dist[0], pos[0][0], pos[0][1], 0.f, reps[0], rot[0], rot[0]); } diff --git a/gfx/gl/shaders/networkStraight.vs b/gfx/gl/shaders/networkStraight.vert index 55f9c4f..91ba955 100644 --- a/gfx/gl/shaders/networkStraight.vs +++ b/gfx/gl/shaders/networkStraight.vert @@ -1,12 +1,10 @@ -#version 330 core +#version 460 core -layout(location = 0) in ivec3 v_apos; -layout(location = 1) in ivec3 v_bpos; +layout(location = 0) in ivec3 v_pos[2]; layout(location = 2) in mat2 v_rot; layout(location = 4) in float v_reps; -flat out ivec3 apos; -flat out ivec3 bpos; +flat out ivec3 pos[2]; flat out mat2 rot; flat out float reps; flat out float dist; @@ -16,9 +14,8 @@ include(`networkCommon.glsl') void main() { - apos = v_apos; - bpos = v_bpos; + pos = v_pos; rot = v_rot; reps = v_reps; - dist = segDist(v_apos, v_bpos); + dist = segDist(v_pos[0], v_pos[1]); } diff --git a/gfx/gl/shaders/pointLight.fs b/gfx/gl/shaders/pointLight.frag index 7531d3e..bb2c453 100644 --- a/gfx/gl/shaders/pointLight.fs +++ b/gfx/gl/shaders/pointLight.frag @@ -1,5 +1,4 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core out vec3 FragColor; diff --git a/gfx/gl/shaders/pointLight.gs b/gfx/gl/shaders/pointLight.geom index fc1d7c3..1ee7e06 100644 --- a/gfx/gl/shaders/pointLight.gs +++ b/gfx/gl/shaders/pointLight.geom @@ -1,6 +1,4 @@ -#version 330 core -#extension GL_ARB_enhanced_layouts : enable -#extension GL_ARB_shading_language_420pack : enable +#version 460 core const vec3[] cube = vec3[]( // http://www.cs.umd.edu/gvil/papers/av_ts.pdf vec3(-1, 1, 1), // Front-top-left diff --git a/gfx/gl/shaders/pointLight.vs b/gfx/gl/shaders/pointLight.vert index fbd031c..2fd5418 100644 --- a/gfx/gl/shaders/pointLight.vs +++ b/gfx/gl/shaders/pointLight.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core layout(location = 0) in vec3 v_position; layout(location = 1) in vec3 v_colour; diff --git a/gfx/gl/shaders/shadowDynamicPoint.vs b/gfx/gl/shaders/shadowDynamicPoint.vert index 7335b9a..907378e 100644 --- a/gfx/gl/shaders/shadowDynamicPoint.vs +++ b/gfx/gl/shaders/shadowDynamicPoint.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core include(`meshIn.glsl') diff --git a/gfx/gl/shaders/shadowDynamicPointInst.vs b/gfx/gl/shaders/shadowDynamicPointInst.vert index d0eb649..da4a76f 100644 --- a/gfx/gl/shaders/shadowDynamicPointInst.vs +++ b/gfx/gl/shaders/shadowDynamicPointInst.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core include(`meshIn.glsl') diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag new file mode 100644 index 0000000..cad9d9d --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag @@ -0,0 +1,18 @@ +#version 460 core + +layout(binding = 3) uniform sampler2D textureAlbedo; + +include(`materialInterface.glsl') +include(`materialCommon.glsl') + +in vec2 texCoord; +flat in MaterialDetail material; + +void +main() +{ + if (getTextureColour(material, texCoord).a < 0.5) { + discard; + } + gl_FragDepth = gl_FragCoord.z; +} diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs deleted file mode 100644 index 90519e3..0000000 --- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs +++ /dev/null @@ -1,15 +0,0 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable - -layout(binding = 3) uniform sampler2D texture0; - -in vec2 texCoord; - -void -main() -{ - if (texture(texture0, texCoord).a < 0.5) { - discard; - } - gl_FragDepth = gl_FragCoord.z; -} diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom new file mode 100644 index 0000000..3aaf9a8 --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom @@ -0,0 +1,3 @@ +define(`TEXTURES', 1) + +include(`commonShadowPoint.geom') diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs deleted file mode 100644 index e6e213e..0000000 --- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs +++ /dev/null @@ -1,3 +0,0 @@ -define(`TEXTURES', 1) - -include(`commonShadowPoint.gs') diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert new file mode 100644 index 0000000..41a47b0 --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert @@ -0,0 +1,14 @@ +#version 460 core + +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(`commonShadowPoint.glsl') diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs deleted file mode 100644 index 27ad9d7..0000000 --- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs +++ /dev/null @@ -1,3 +0,0 @@ -define(`TEXTURES', 1) - -include(`shadowDynamicPointInst.vs') diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.frag b/gfx/gl/shaders/shadowDynamicPointStencil.frag new file mode 100644 index 0000000..d6b8a0e --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.frag @@ -0,0 +1,15 @@ +#version 460 core + +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.geom b/gfx/gl/shaders/shadowDynamicPointStencil.geom new file mode 100644 index 0000000..df8be8d --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.geom @@ -0,0 +1,35 @@ +#version 460 core + +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.vert b/gfx/gl/shaders/shadowDynamicPointStencil.vert new file mode 100644 index 0000000..b750b3e --- /dev/null +++ b/gfx/gl/shaders/shadowDynamicPointStencil.vert @@ -0,0 +1,16 @@ +#version 460 core + +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/shadowLandmass.vs b/gfx/gl/shaders/shadowLandmass.vert index becf142..cf68fe5 100644 --- a/gfx/gl/shaders/shadowLandmass.vs +++ b/gfx/gl/shaders/shadowLandmass.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core layout(location = 0) in ivec3 position; diff --git a/gfx/gl/shaders/shadowStencil.frag b/gfx/gl/shaders/shadowStencil.frag new file mode 100644 index 0000000..3269f69 --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.frag @@ -0,0 +1,17 @@ +#version 460 core + +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.geom b/gfx/gl/shaders/shadowStencil.geom new file mode 100644 index 0000000..53a4c19 --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.geom @@ -0,0 +1,27 @@ +#version 460 core + +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.vert b/gfx/gl/shaders/shadowStencil.vert new file mode 100644 index 0000000..e6286e6 --- /dev/null +++ b/gfx/gl/shaders/shadowStencil.vert @@ -0,0 +1,19 @@ +#version 460 core + +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/shaders/spotLight.fs b/gfx/gl/shaders/spotLight.frag index ad33458..1305dbd 100644 --- a/gfx/gl/shaders/spotLight.fs +++ b/gfx/gl/shaders/spotLight.frag @@ -1,5 +1,4 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core out vec3 FragColor; diff --git a/gfx/gl/shaders/spotLight.gs b/gfx/gl/shaders/spotLight.geom index 194812a..fec191e 100644 --- a/gfx/gl/shaders/spotLight.gs +++ b/gfx/gl/shaders/spotLight.geom @@ -1,6 +1,4 @@ -#version 330 core -#extension GL_ARB_enhanced_layouts : enable -#extension GL_ARB_shading_language_420pack : enable +#version 460 core const vec3[] pyramid = vec3[]( // four-sided vec3(0, 0, 0), // Apex diff --git a/gfx/gl/shaders/spotLight.vs b/gfx/gl/shaders/spotLight.vert index e0196c3..36d2ee5 100644 --- a/gfx/gl/shaders/spotLight.vs +++ b/gfx/gl/shaders/spotLight.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core layout(location = 0) in vec3 v_position; layout(location = 1) in vec3 v_direction; diff --git a/gfx/gl/shaders/uiShader.fs b/gfx/gl/shaders/uiShader.fs deleted file mode 100644 index c5f4e92..0000000 --- a/gfx/gl/shaders/uiShader.fs +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 core - -in vec2 texCoord0; - -uniform sampler2D sampler; - -void -main() -{ - gl_FragColor = texture(sampler, texCoord0); -} diff --git a/gfx/gl/shaders/uiShader.vs b/gfx/gl/shaders/uiShader.vs deleted file mode 100644 index e9e4373..0000000 --- a/gfx/gl/shaders/uiShader.vs +++ /dev/null @@ -1,13 +0,0 @@ -#version 330 core - -in vec4 position; - -out vec2 texCoord0; -uniform mat4 uiProjection; - -void -main() -{ - gl_Position = uiProjection * vec4(position.xy, 0.0, 1.0); - texCoord0 = position.zw; -} diff --git a/gfx/gl/shaders/uiShaderFont.fs b/gfx/gl/shaders/uiShaderFont.fs deleted file mode 100644 index a1ef6ef..0000000 --- a/gfx/gl/shaders/uiShaderFont.fs +++ /dev/null @@ -1,12 +0,0 @@ -#version 330 core - -in vec2 texCoord0; - -uniform sampler2D sampler; -uniform vec3 colour; - -void -main() -{ - gl_FragColor = vec4(colour, texture(sampler, texCoord0).r); -} diff --git a/gfx/gl/shaders/water.fs b/gfx/gl/shaders/water.frag index 0918d9f..a09e754 100644 --- a/gfx/gl/shaders/water.fs +++ b/gfx/gl/shaders/water.frag @@ -1,5 +1,4 @@ -#version 330 core -#extension GL_ARB_shading_language_420pack : enable +#version 460 core in vec3 FragPos; in vec2 TexCoords; diff --git a/gfx/gl/shaders/water.vs b/gfx/gl/shaders/water.vert index 58bf7b6..60e4fb8 100644 --- a/gfx/gl/shaders/water.vs +++ b/gfx/gl/shaders/water.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 460 core layout(location = 0) in ivec3 position; out vec3 FragPos; diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index a846a3d..7035162 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -1,30 +1,39 @@ #include "shadowMapper.h" -#include "camera.h" #include "collections.h" -#include "gfx/gl/shaders/fs-shadowDynamicPointInstWithTextures.h" -#include "gfx/gl/shaders/gs-commonShadowPoint.h" -#include "gfx/gl/shaders/gs-shadowDynamicPointInstWithTextures.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-shadowLandmass.h" +#include "game/gamestate.h" +#include "gfx/aabb.h" +#include "gfx/gl/shadowStenciller.h" +#include "gfx/lightDirection.h" +#include "gfx/renderable.h" #include "gl_traits.h" +#include "gldebug.h" #include "location.h" #include "maths.h" #include "sceneProvider.h" #include "sceneShader.h" -#include "sorting.h" +#include <gfx/camera.h> +#include <gfx/gl/shaders/commonShadowPoint-geom.h> +#include <gfx/gl/shaders/shadowDynamicPoint-vert.h> +#include <gfx/gl/shaders/shadowDynamicPointInst-vert.h> +#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-frag.h> +#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-geom.h> +#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-vert.h> +#include <gfx/gl/shaders/shadowDynamicPointStencil-frag.h> +#include <gfx/gl/shaders/shadowDynamicPointStencil-geom.h> +#include <gfx/gl/shaders/shadowDynamicPointStencil-vert.h> +#include <gfx/gl/shaders/shadowLandmass-vert.h> #include <glm/gtc/type_ptr.hpp> #include <glm/gtx/transform.hpp> #include <glm/matrix.hpp> #include <vector> ShadowMapper::ShadowMapper(const TextureAbsCoord & s) : - landmess {shadowLandmass_vs}, dynamicPointInst {shadowDynamicPointInst_vs}, - dynamicPointInstWithTextures {shadowDynamicPointInstWithTextures_vs, shadowDynamicPointInstWithTextures_gs, - shadowDynamicPointInstWithTextures_fs}, + landmess {shadowLandmass_vert}, dynamicPointInst {shadowDynamicPointInst_vert}, + dynamicPointInstWithTextures {shadowDynamicPointInstWithTextures_vert, shadowDynamicPointInstWithTextures_geom, + shadowDynamicPointInstWithTextures_frag}, size {s} { + glDebugScope _ {depthMap}; glBindTexture(GL_TEXTURE_2D_ARRAY, depthMap); glTexImage3D( GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT, size.x, size.y, 4, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); @@ -45,13 +54,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)))...}; + }(4.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,43 +83,59 @@ 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 { + glDebugScope _ {depthMap}; + glCullFace(GL_FRONT); + glEnable(GL_DEPTH_TEST); + + shadowStenciller.setLightDirection(dir); + for (const auto & [id, asset] : gameState->assets) { + if (const auto r = asset.getAs<const Renderable>()) { + 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; - 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)); - }; - - 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})); - + Sizes sizes; + using ExtentsBoundingBox = AxisAlignedBoundingBox<RelativeDistance>; + std::ranges::transform(bandViewExtents | std::views::pairwise, std::back_inserter(out), + [&lightViewDir, &sizes](const auto & band) mutable { + const auto & [near, far] = band; + auto extents = ExtentsBoundingBox::fromPoints(std::span {near.begin(), far.end()}); + extents.min.z -= 10'000.F; + extents.max.z += 10'000.F; + const auto lightProjection = glm::ortho( + extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z); + sizes.emplace_back(extents.max - extents.min); 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); + } + ExtentsBoundingBox extents {lightViewPoint, lightViewPoint}; + for (const auto & point : bandViewExtents.back()) { + extents += point; } - scene.shadows(*this); + const auto lightProjection + = glm::ortho(extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z); + Frustum frustum {lightViewPoint, lightViewDir, lightProjection}; + scene.shadows(*this, frustum); glCullFace(GL_BACK); return out; } -ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs) : Program {vs, commonShadowPoint_gs} { } +ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs) : Program {vs, commonShadowPoint_geom} { } ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs, const Shader & gs, const Shader & fs) : Program {vs, gs, fs} @@ -116,12 +143,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())); } @@ -131,7 +161,7 @@ ShadowMapper::ShadowProgram::use() const glUseProgram(*this); } -ShadowMapper::DynamicPoint::DynamicPoint() : ShadowProgram {shadowDynamicPoint_vs} { } +ShadowMapper::DynamicPoint::DynamicPoint() : ShadowProgram {shadowDynamicPoint_vert} { } void ShadowMapper::DynamicPoint::use(const Location & location) const @@ -146,3 +176,16 @@ ShadowMapper::DynamicPoint::setModel(const Location & location) const glUniform(modelLoc, location.getRotationTransform()); glUniform(modelPosLoc, location.pos); } + +ShadowMapper::StencilShadowProgram::StencilShadowProgram() : + ShadowProgram {shadowDynamicPointStencil_vert, shadowDynamicPointStencil_geom, shadowDynamicPointStencil_frag} +{ +} + +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..aee7161 --- /dev/null +++ b/gfx/gl/shadowStenciller.cpp @@ -0,0 +1,76 @@ +#include "shadowStenciller.h" +#include "gfx/lightDirection.h" +#include "gfx/models/mesh.h" +#include "glArrays.h" +#include "gl_traits.h" +#include "gldebug.h" +#include "maths.h" +#include <gfx/gl/shaders/shadowStencil-frag.h> +#include <gfx/gl/shaders/shadowStencil-geom.h> +#include <gfx/gl/shaders/shadowStencil-vert.h> +#include <stdexcept> + +ShadowStenciller::ShadowStenciller() : + shadowCaster {shadowStencil_vert, shadowStencil_geom, shadowStencil_frag}, viewProjections {} +{ + glDebugScope _ {fbo}; + 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 +{ + glDebugScope _ {fbo}; + 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; +}; diff --git a/gfx/gl/uiShader.cpp b/gfx/gl/uiShader.cpp deleted file mode 100644 index 23da9dc..0000000 --- a/gfx/gl/uiShader.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "uiShader.h" -#include "gl_traits.h" -#include <gfx/gl/program.h> -#include <gfx/gl/shader.h> -#include <gfx/gl/shaders/fs-uiShader.h> -#include <gfx/gl/shaders/fs-uiShaderFont.h> -#include <gfx/gl/shaders/vs-uiShader.h> -#include <glm/glm.hpp> -#include <glm/gtc/type_ptr.hpp> - -UIShader::IconProgram::IconProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShader_fs} { } - -UIShader::TextProgram::TextProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShaderFont_fs} { } - -UIShader::UIShader(size_t width, size_t height) : - UIShader {glm::ortho<float>(0, static_cast<float>(width), 0, static_cast<float>(height))} -{ -} - -UIShader::UIShader(const glm::mat4 & viewProjection) : icon {viewProjection}, text {viewProjection} { } - -void -UIShader::TextProgram::use(const RGB & colour) const -{ - Program::use(); - glUniform(colorLoc, colour); -} diff --git a/gfx/gl/uiShader.h b/gfx/gl/uiShader.h deleted file mode 100644 index 6d00166..0000000 --- a/gfx/gl/uiShader.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "config/types.h" -#include "gl_traits.h" -#include "program.h" -#include <cstddef> -#include <glad/gl.h> -#include <glm/glm.hpp> -#include <glm/gtc/type_ptr.hpp> - -class UIShader { -public: - UIShader(std::size_t width, std::size_t height); - -private: - explicit UIShader(const glm::mat4 & viewProjection); - - class UIProgram : public Program { - public: - template<typename... S> - explicit UIProgram(const glm::mat4 & vp, S &&... srcs) : Program {std::forward<S>(srcs)...} - { - const RequiredUniformLocation uiProjectionLoc {*this, "uiProjection"}; - glUseProgram(*this); - glUniform(uiProjectionLoc, vp); - } - }; - - class IconProgram : public UIProgram { - public: - explicit IconProgram(const glm::mat4 & vp); - using Program::use; - }; - - class TextProgram : public UIProgram { - public: - explicit TextProgram(const glm::mat4 & vp); - void use(const RGB & colour) const; - - private: - RequiredUniformLocation colorLoc {*this, "colour"}; - }; - -public: - IconProgram icon; - TextProgram text; -}; diff --git a/gfx/gl/vertexArrayObject.h b/gfx/gl/vertexArrayObject.h index 57daaf3..d008897 100644 --- a/gfx/gl/vertexArrayObject.h +++ b/gfx/gl/vertexArrayObject.h @@ -21,14 +21,15 @@ public: NO_COPY(VertexArrayObject); template<typename m, typename T> struct MP { - constexpr MP(m T::*p) : P {p} { } + constexpr MP(m T::* p) : P {p} { } + constexpr operator void *() const { return &(static_cast<T *>(nullptr)->*P); } - m T::*P; + m T::* P; using value_type = m; }; @@ -69,6 +70,13 @@ public: return *this; } + VertexArrayObject & + data(const GLuint arrayBuffer, GLenum target) + { + glBindBuffer(target, arrayBuffer); + return *this; + } + template<typename Data> static void data(const Data & data, const GLuint arrayBuffer, GLenum target) diff --git a/gfx/lightDirection.cpp b/gfx/lightDirection.cpp new file mode 100644 index 0000000..3932872 --- /dev/null +++ b/gfx/lightDirection.cpp @@ -0,0 +1,12 @@ +#include "lightDirection.h" +#include "maths.h" + +constexpr auto ASTRONOMICAL_TWILIGHT = 18.0_degrees; +constexpr auto SUN_ANGLUAR_SIZE = 0.5_degrees; + +LightDirection::LightDirection(const Direction2D sunPos) : + pos {sunPos}, vec {glm::mat3 {rotate_yp(pi + sunPos.x, -sunPos.y)} * north}, + amb {glm::clamp(sunPos.y + ASTRONOMICAL_TWILIGHT, 0.F, 1.F)}, + dir {glm::clamp(sunPos.y + SUN_ANGLUAR_SIZE, 0.F, 1.F)} +{ +} diff --git a/gfx/lightDirection.h b/gfx/lightDirection.h new file mode 100644 index 0000000..789830b --- /dev/null +++ b/gfx/lightDirection.h @@ -0,0 +1,39 @@ +#pragma once + +#include "config/types.h" + +class LightDirection { +public: + // NOLINTNEXTLINE(hicpp-explicit-conversions) deliberately a helper + LightDirection(Direction2D sunPos); + + [[nodiscard]] Direction2D + position() const noexcept + { + return pos; + } + + [[nodiscard]] Direction3D + vector() const noexcept + { + return vec; + } + + [[nodiscard]] float + ambient() const noexcept + { + return amb; + } + + [[nodiscard]] float + directional() const noexcept + { + return dir; + } + +private: + Direction2D pos; + Direction3D vec; + float amb; + float dir; +}; diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index e7474ca..2eae160 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -1,6 +1,32 @@ #include "mesh.h" -MeshBase::MeshBase(GLsizei m_numIndices, GLenum mode) : m_numIndices {m_numIndices}, mode {mode} { } +MeshBase::MeshBase(GLsizei m_numIndices, GLenum mode, const std::vector<RelativePosition3D> & positions) : + m_numIndices {m_numIndices}, mode {mode}, dimensions {positions} +{ +} + +MeshBase::Dimensions::Dimensions(const std::span<const RelativePosition3D> positions) : + Dimensions {positions, {extents(positions, 0), extents(positions, 1), extents(positions, 2)}} +{ +} + +MeshBase::Dimensions::Dimensions( + const std::span<const RelativePosition3D> positions, const std::array<Extents1D, 3> & extents1ds) : + minExtent(extents1ds[0].min, extents1ds[1].min, extents1ds[2].min), + maxExtent(extents1ds[0].max, extents1ds[1].max, extents1ds[2].max), centre {(minExtent + maxExtent) / 2.0F}, + size {std::ranges::max(positions | std::views::transform([this](const auto & v) { + return glm::distance(v, centre); + }))} +{ +} + +MeshBase::Dimensions::Extents1D +MeshBase::Dimensions::extents(const std::span<const RelativePosition3D> positions, glm::length_t D) +{ + return std::ranges::minmax(positions | std::views::transform([D](const auto & v) { + return v[D]; + })); +} void MeshBase::Draw() const diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index 248cb8f..8791aed 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -1,8 +1,10 @@ #pragma once +#include "config/types.h" #include "gfx/gl/vertexArrayObject.h" #include <glArrays.h> #include <glad/gl.h> +#include <ranges> #include <span> #include <stdTypeDefs.h> @@ -10,22 +12,46 @@ class Vertex; class MeshBase { public: + class Dimensions { + public: + using Extents1D = std::ranges::minmax_result<RelativeDistance>; + explicit Dimensions(const std::span<const RelativePosition3D>); + + RelativePosition3D minExtent, maxExtent; + RelativePosition3D centre; + RelativeDistance size; + + private: + Dimensions(const std::span<const RelativePosition3D>, const std::array<Extents1D, 3> &); + static Extents1D extents(const std::span<const RelativePosition3D>, glm::length_t D); + }; + void Draw() const; void DrawInstanced(GLuint vao, GLsizei count, GLuint base = 0) const; + [[nodiscard]] const Dimensions & + getDimensions() const + { + return dimensions; + } + protected: - MeshBase(GLsizei m_numIndices, GLenum mode); + MeshBase(GLsizei m_numIndices, GLenum mode, const std::vector<RelativePosition3D> &); glVertexArray m_vertexArrayObject; glBuffers<2> m_vertexArrayBuffers; GLsizei m_numIndices; GLenum mode; + Dimensions dimensions; }; template<typename V> class MeshT : public MeshBase, public ConstTypeDefs<MeshT<V>> { public: MeshT(const std::span<const V> vertices, const std::span<const unsigned int> indices, GLenum mode = GL_TRIANGLES) : - MeshBase {static_cast<GLsizei>(indices.size()), mode} + MeshBase {static_cast<GLsizei>(indices.size()), mode, + materializeRange(vertices | std::views::transform([](const auto & v) { + return static_cast<RelativePosition3D>(v.pos); + }))} { VertexArrayObject::data(vertices, m_vertexArrayBuffers[0], GL_ARRAY_BUFFER); VertexArrayObject::data(indices, m_vertexArrayBuffers[1], GL_ARRAY_BUFFER); diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index 51223aa..3457fb5 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -50,6 +50,16 @@ Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOption glTexParameter(type, GL_TEXTURE_MIN_FILTER, to.minFilter); glTexParameter(type, GL_TEXTURE_MAG_FILTER, to.magFilter); glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + auto isMimmap = [](auto value) { + auto eqAnyOf = [value](auto... test) { + return (... || (value == test)); + }; + return eqAnyOf( + GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR); + }; + if (isMimmap(to.minFilter) || isMimmap(to.magFilter)) { + glGenerateMipmap(type); + } } void @@ -59,12 +69,13 @@ Texture::bind(GLenum unit) const glBindTexture(type, m_texture); } -TextureAbsCoord +TextureDimensions Texture::getSize(const glTexture & texture) { - TextureAbsCoord size; + TextureDimensions size {}; glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_WIDTH, &size.x); glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_HEIGHT, &size.y); + glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_DEPTH, &size.z); return size; } @@ -73,7 +84,7 @@ Texture::save( const glTexture & texture, GLenum format, GLenum type, uint8_t channels, const char * path, uint8_t tgaFormat) { const auto size = getSize(texture); - const size_t dataSize = (static_cast<size_t>(size.x * size.y * channels)); + const size_t dataSize = (static_cast<size_t>(size.x * size.y * size.z * channels)); const size_t fileSize = dataSize + sizeof(TGAHead); filesystem::fh out {path, O_RDWR | O_CREAT, 0660}; @@ -81,7 +92,7 @@ Texture::save( auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED); *tga.get<TGAHead>() = { .format = tgaFormat, - .size = size, + .size = {size.x, size.y * size.z}, .pixelDepth = static_cast<uint8_t>(8 * channels), }; glPixelStorei(GL_PACK_ALIGNMENT, 1); diff --git a/gfx/models/texture.h b/gfx/models/texture.h index 689d378..d8c3b29 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -1,8 +1,9 @@ #pragma once #include "config/types.h" +#include "glArrays.h" +#include "stdTypeDefs.h" #include <filesystem> -#include <glArrays.h> #include <glm/fwd.hpp> class Image; @@ -20,7 +21,7 @@ struct TextureOptions { static GLint glMapMode(MapMode); }; -class Texture { +class Texture : public StdTypeDefs<Texture> { public: virtual ~Texture() = default; DEFAULT_MOVE_NO_COPY(Texture); @@ -37,10 +38,10 @@ public: static void saveDepth(const glTexture &, const char * path); static void saveNormal(const glTexture &, const char * path); static void savePosition(const glTexture &, const char * path); + static TextureDimensions getSize(const glTexture &); protected: static void save(const glTexture &, GLenum, GLenum, uint8_t channels, const char * path, uint8_t tgaFormat); - static TextureAbsCoord getSize(const glTexture &); glTexture m_texture; GLenum type; diff --git a/gfx/renderable.cpp b/gfx/renderable.cpp index 0340189..27f2459 100644 --- a/gfx/renderable.cpp +++ b/gfx/renderable.cpp @@ -6,6 +6,11 @@ Renderable::lights(const SceneShader &) const } void -Renderable::shadows(const ShadowMapper &) const +Renderable::shadows(const ShadowMapper &, const Frustum &) const +{ +} + +void +Renderable::updateStencil(const ShadowStenciller &) const { } diff --git a/gfx/renderable.h b/gfx/renderable.h index e126fff..140c570 100644 --- a/gfx/renderable.h +++ b/gfx/renderable.h @@ -3,7 +3,9 @@ #include <special_members.h> class SceneShader; +class Frustum; class ShadowMapper; +class ShadowStenciller; class Renderable { public: @@ -11,7 +13,9 @@ public: virtual ~Renderable() = default; DEFAULT_MOVE_COPY(Renderable); - virtual void render(const SceneShader & shader) const = 0; + virtual void render(const SceneShader & shader, const Frustum &) const = 0; virtual void lights(const SceneShader & shader) const; - virtual void shadows(const ShadowMapper & shadowMapper) const; + virtual void shadows(const ShadowMapper & shadowMapper, const Frustum &) const; + + virtual void updateStencil(const ShadowStenciller & lightDir) const; }; |
