From 971994f8f4c4b06d441e37dc1d9c997eb5ec5526 Mon Sep 17 00:00:00 2001 From: Franciszek Szewczyk Date: Sat, 9 Nov 2024 20:11:06 +0100 Subject: [PATCH 1/2] Directional Light Shadows --- resources/shaders/fragment/depth.glsl | 9 + resources/shaders/fragment/uber.glsl | 160 ++++++++++++++++-- resources/shaders/vertex/shadowmap.glsl | 21 +++ src/AssetManager/Material.cpp | 18 +- src/AssetManager/Material.hpp | 7 +- src/AssetManager/Mesh.cpp | 17 ++ src/AssetManager/Mesh.hpp | 1 + src/Components/CameraComponent.hpp | 1 - src/Components/DirectionalLightComponent.hpp | 42 +++++ src/Components/TransformComponent.hpp | 2 +- src/Rendering/CMakeLists.txt | 4 +- src/Rendering/FrameBuffer.cpp | 64 ------- src/Rendering/FrameBuffer.hpp | 31 ---- .../FrameBuffers/DepthFrameBuffer.cpp | 111 ++++++++++++ .../FrameBuffers/DepthFrameBuffer.hpp | 38 +++++ .../FrameBuffers/SceneFrameBuffer.cpp | 108 ++++++++++++ .../FrameBuffers/SceneFrameBuffer.hpp | 38 +++++ src/Rendering/Texture.cpp | 26 ++- src/Rendering/Texture.hpp | 3 + src/Rendering/Utils.hpp | 6 +- src/Runtime/Runtime.hpp | 2 +- src/Systems/RenderingSystem.cpp | 95 +++++++++-- src/Systems/RenderingSystem.hpp | 18 +- src/main.cpp | 33 +++- src/ui/UI.cpp | 4 +- 25 files changed, 695 insertions(+), 164 deletions(-) create mode 100644 resources/shaders/fragment/depth.glsl create mode 100644 resources/shaders/vertex/shadowmap.glsl delete mode 100644 src/Rendering/FrameBuffer.cpp delete mode 100644 src/Rendering/FrameBuffer.hpp create mode 100644 src/Rendering/FrameBuffers/DepthFrameBuffer.cpp create mode 100644 src/Rendering/FrameBuffers/DepthFrameBuffer.hpp create mode 100644 src/Rendering/FrameBuffers/SceneFrameBuffer.cpp create mode 100644 src/Rendering/FrameBuffers/SceneFrameBuffer.hpp diff --git a/resources/shaders/fragment/depth.glsl b/resources/shaders/fragment/depth.glsl new file mode 100644 index 00000000..3d6dfc27 --- /dev/null +++ b/resources/shaders/fragment/depth.glsl @@ -0,0 +1,9 @@ +#version 330 core + +in float FragDepth; + +out float FragValue; + +void main() { + FragValue = FragDepth; +} diff --git a/resources/shaders/fragment/uber.glsl b/resources/shaders/fragment/uber.glsl index bda13827..73db1e4b 100644 --- a/resources/shaders/fragment/uber.glsl +++ b/resources/shaders/fragment/uber.glsl @@ -14,31 +14,41 @@ struct PointLight { }; struct DirectionalLight { + sampler2D shadowSampler[4]; + mat4 lightSpaceMatrix[4]; vec3 direction; // Light position vec3 color; // Diffuse light color }; +struct LodBlend { + int highLod; + int lowLod; + float highLodWeight; +}; + uniform vec3 ambientLight; uniform int numPointLights; uniform PointLight pointLights[16]; uniform int numDirectionalLights; -uniform DirectionalLight directionalLights[16]; +uniform DirectionalLight directionalLights[2]; // Material structure struct Material { - vec3 diffuse; // Diffuse material color - vec3 specular; // Specular material color + vec3 color; // Diffuse material color float shininess; // Shininess factor }; uniform Material material; -void main() { - vec3 result = ambientLight * material.diffuse; - vec3 normal = normalize(Normal); +vec3 calculateAmbient() { + return ambientLight * material.color; +} +vec3 calculatePointLights() { + vec3 normal = normalize(Normal); + vec3 result = vec3(0.0); for (int i = 0; i < numPointLights; ++i) { float relativeIntensity = 1.0 - (length(pointLights[i].position - FragPos) / pointLights[i].range); @@ -47,32 +57,150 @@ void main() { // Diffuse vec3 lightDir = normalize(pointLights[i].position - FragPos); float diff = max(dot(normal, lightDir), 0.0); - vec3 diffuse = pointLights[i].color * diff * material.diffuse; + vec3 diffuse = pointLights[i].color * diff * material.color; // Specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); - vec3 specular = pointLights[i].color * spec * material.specular; + vec3 specular = pointLights[i].color * spec * material.color; result += relativeIntensity * (diffuse + specular); } } + return result; +} + +// Calculate the shadow factor with PCF and LOD blending +float calculateShadowFactor(int directionalLightIndex, LodBlend lodBlend) { + // Transform fragment position to light space for low LOD + vec4 fragPosLightSpaceLowLod = directionalLights[directionalLightIndex] + .lightSpaceMatrix[lodBlend.lowLod] * + vec4(FragPos, 1.0); + vec3 projCoordsLowLod = + fragPosLightSpaceLowLod.xyz / fragPosLightSpaceLowLod.w; + projCoordsLowLod = projCoordsLowLod * 0.5 + 0.5; // Transform to [0, 1] range + + // Transform fragment position to light space for high LOD + vec4 fragPosLightSpaceHighLod = directionalLights[directionalLightIndex] + .lightSpaceMatrix[lodBlend.highLod] * + vec4(FragPos, 1.0); + vec3 projCoordsHighLod = + fragPosLightSpaceHighLod.xyz / fragPosLightSpaceHighLod.w; + projCoordsHighLod = + projCoordsHighLod * 0.5 + 0.5; // Transform to [0, 1] range + + // If outside the light's frustum, not in shadow + if (projCoordsLowLod.z > 1.0 || projCoordsHighLod.z > 1.0) { + return 1.0; + } + + // Define PCF kernel size and bias for shadow comparison + float bias = 0.001; + float shadowLowLod = 0.0; + float shadowHighLod = 0.0; + int pcfKernelSize = + 2; // You can adjust this value for more or less smoothing + float texelSize = + 1.0 / 2048.0; // Assume a shadow map resolution of 2048x2048 + + // Loop over the kernel to sample neighboring depths for the low LOD + for (int x = -pcfKernelSize; x <= pcfKernelSize; ++x) { + for (int y = -pcfKernelSize; y <= pcfKernelSize; ++y) { + // Sample the shadow map for the low LOD + float closestDepthLowLod = + texture(directionalLights[directionalLightIndex] + .shadowSampler[lodBlend.lowLod], + projCoordsLowLod.xy + vec2(x, y) * texelSize) + .r; + closestDepthLowLod = + closestDepthLowLod * 0.5 + 0.5; // Remap to [0, 1] range + if (projCoordsLowLod.z - bias > closestDepthLowLod) { + shadowLowLod += 1.0; + } + + // Sample the shadow map for the high LOD + float closestDepthHighLod = + texture(directionalLights[directionalLightIndex] + .shadowSampler[lodBlend.highLod], + projCoordsHighLod.xy + vec2(2 * x, 2 * y) * texelSize) + .r; + closestDepthHighLod = + closestDepthHighLod * 0.5 + 0.5; // Remap to [0, 1] range + if (projCoordsHighLod.z - bias > closestDepthHighLod) { + shadowHighLod += 1.0; + } + } + } + + // Normalize shadow values by the kernel size + shadowLowLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1)); + shadowHighLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1)); + + // Blend the shadow factors between the low and high LOD based on highLodWeight + return mix(1.0 - shadowLowLod, 1.0 - shadowHighLod, lodBlend.highLodWeight); +} + +LodBlend chooseLightLOD(mat4 lightSpaceMatrix[4], vec3 fragPos) { + // Transform fragment position to light space + for (int lod = 0; lod < 4; ++lod) { + vec4 fragPosLightSpace = lightSpaceMatrix[lod] * vec4(fragPos, 1.0); + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + + if (projCoords.x >= 0.0 && projCoords.y >= 0.0 && projCoords.x <= 1.0 && + projCoords.y <= 1.0 && projCoords.z < 1.0) { + if (lod == 3) { + return LodBlend(lod, lod, 1.0); + } else { + float distanceToEdgeX = min(projCoords.x, 1.0 - projCoords.x); + float distanceToEdgeY = min(projCoords.y, 1.0 - projCoords.y); + + float distanceToEdge = min(distanceToEdgeX, distanceToEdgeY); + + float highLodWeight = distanceToEdge / 0.5; + return LodBlend(lod, lod + 1, highLodWeight); + } + } + } + return LodBlend(-1, -1, 0.0); +} + +// Calculates the lighting from directional lights, including shadow mapping +vec3 calculateDirectionalLights() { + vec3 normal = normalize(Normal); + vec3 result = vec3(0.0); for (int i = 0; i < numDirectionalLights; ++i) { - // Diffuse - vec3 lightDir = directionalLights[i].direction; + vec3 lightDir = normalize(directionalLights[i].direction); + + // Diffuse lighting float diff = max(dot(normal, lightDir), 0.0); - vec3 diffuse = directionalLights[i].color * diff * material.diffuse; + vec3 diffuse = directionalLights[i].color * diff * material.color; - // Specular + // Specular lighting vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); - vec3 specular = directionalLights[i].color * spec * material.specular; - - result += diffuse + specular; + vec3 specular = directionalLights[i].color * spec * material.color; + + // Calculate shadow factor + LodBlend lodBlend = + chooseLightLOD(directionalLights[i].lightSpaceMatrix, FragPos); + float shadow = 1.0; + if (lodBlend.lowLod != -1) { + shadow = calculateShadowFactor(i, lodBlend); + } + result += (diffuse + specular) * shadow; } + return result; +} +void main() { + vec3 ambient = calculateAmbient(); + vec3 pointLightResult = calculatePointLights(); + vec3 directionalLightResult = calculateDirectionalLights(); + + vec3 result = ambient + pointLightResult + directionalLightResult; FragColor = vec4(result, 1.0); -} +} \ No newline at end of file diff --git a/resources/shaders/vertex/shadowmap.glsl b/resources/shaders/vertex/shadowmap.glsl new file mode 100644 index 00000000..21e7821b --- /dev/null +++ b/resources/shaders/vertex/shadowmap.glsl @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec3 aPos; // Vertex position + +uniform mat4 modelMatrix; // Model matrix +uniform mat4 viewMatrix; // View matrix +uniform mat4 projectionMatrix; // Projection matrix + +out float FragDepth; // Output the depth value to the fragment shader + +void main() { + // Transform vertex position to world space, then to light space + vec4 lightSpacePosition = + projectionMatrix * viewMatrix * modelMatrix * vec4(aPos, 1.0); + + // Set the final position + gl_Position = lightSpacePosition; + + // Calculate the depth in light space (NDC) range [0,1] + FragDepth = lightSpacePosition.z / lightSpacePosition.w; +} diff --git a/src/AssetManager/Material.cpp b/src/AssetManager/Material.cpp index 19a09b35..5cd48889 100644 --- a/src/AssetManager/Material.cpp +++ b/src/AssetManager/Material.cpp @@ -3,10 +3,10 @@ namespace shkyera { Material::Material() - : diffuseColor(1.0f, 1.0f, 1.0f), specularColor(1.0f, 1.0f, 1.0f), shininess(32.0f) {} + : diffuseColor(1.0f, 1.0f, 1.0f), shininess(32.0f) {} -Material::Material(const glm::vec3& diffuse, const glm::vec3& specular, float shininess) - : diffuseColor(diffuse), specularColor(specular), shininess(shininess) {} +Material::Material(const glm::vec3& diffuse, float shininess) + : diffuseColor(diffuse), shininess(shininess) {} const glm::vec3& Material::getDiffuseColor() const { return diffuseColor; @@ -16,14 +16,6 @@ glm::vec3& Material::getDiffuseColor() { return diffuseColor; } -const glm::vec3& Material::getSpecularColor() const { - return specularColor; -} - -glm::vec3& Material::getSpecularColor() { - return specularColor; -} - float Material::getShininess() const { return shininess; } @@ -37,10 +29,6 @@ void Material::setDiffuseColor(const glm::vec3& diffuse) { diffuseColor = diffuse; } -void Material::setSpecularColor(const glm::vec3& specular) { - specularColor = specular; -} - void Material::setShininess(float shininessValue) { shininess = shininessValue; } diff --git a/src/AssetManager/Material.hpp b/src/AssetManager/Material.hpp index 881525fa..fbe3fa16 100644 --- a/src/AssetManager/Material.hpp +++ b/src/AssetManager/Material.hpp @@ -9,24 +9,19 @@ class Material : public Asset { public: // Constructors Material(); - Material(const glm::vec3& diffuse, const glm::vec3& specular, float shininess); + Material(const glm::vec3& diffuse, float shininess); const glm::vec3& getDiffuseColor() const; glm::vec3& getDiffuseColor(); - const glm::vec3& getSpecularColor() const; - glm::vec3& getSpecularColor(); - float getShininess() const; float& getShininess(); void setDiffuseColor(const glm::vec3& diffuse); - void setSpecularColor(const glm::vec3& specular); void setShininess(float shininessValue); private: glm::vec3 diffuseColor; - glm::vec3 specularColor; float shininess; }; diff --git a/src/AssetManager/Mesh.cpp b/src/AssetManager/Mesh.cpp index 56bdae74..2a02dfd4 100644 --- a/src/AssetManager/Mesh.cpp +++ b/src/AssetManager/Mesh.cpp @@ -196,6 +196,23 @@ void Mesh::uploadToGPU() { glBindVertexArray(0); } + +Mesh* Mesh::Factory::createPlane() { + std::vector vertices = { + { { -1.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, // 0 + { { 1.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, // 1 + { { 1.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 1.0f } }, // 2 + { { -1.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f } } // 3 + }; + + std::vector indices = { + 0, 1, 2, + 2, 3, 0 + }; + + return new Mesh(vertices, indices); +} + Mesh* Mesh::Factory::createCube() { std::vector vertices = { // Front face diff --git a/src/AssetManager/Mesh.hpp b/src/AssetManager/Mesh.hpp index 09efe90e..44338e86 100644 --- a/src/AssetManager/Mesh.hpp +++ b/src/AssetManager/Mesh.hpp @@ -44,6 +44,7 @@ class Mesh : public Asset { class Factory { public: + static Mesh* createPlane(); static Mesh* createCube(); static Mesh* createCubeMap(); static Mesh* createCylinder(); diff --git a/src/Components/CameraComponent.hpp b/src/Components/CameraComponent.hpp index 80849588..ed309898 100644 --- a/src/Components/CameraComponent.hpp +++ b/src/Components/CameraComponent.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/src/Components/DirectionalLightComponent.hpp b/src/Components/DirectionalLightComponent.hpp index b2af3f80..945efa3a 100644 --- a/src/Components/DirectionalLightComponent.hpp +++ b/src/Components/DirectionalLightComponent.hpp @@ -4,7 +4,9 @@ #include #include + #include +#include namespace shkyera { @@ -12,6 +14,46 @@ class DirectionalLightComponent : public BaseComponent { glm::vec3& getOrientation() { return _orientation; } const glm::vec3& getOrientation() const { return _orientation; } - void getOrientation(const glm::vec3& orientation) { _orientation = orientation; } + void setOrientation(const glm::vec3& orientation) { _orientation = orientation; } glm::vec3& getScale() { return _scale; } const glm::vec3& getScale() const { return _scale; } diff --git a/src/Rendering/CMakeLists.txt b/src/Rendering/CMakeLists.txt index 0cc68799..5fe39004 100644 --- a/src/Rendering/CMakeLists.txt +++ b/src/Rendering/CMakeLists.txt @@ -3,10 +3,12 @@ add_library( STATIC ${CMAKE_CURRENT_LIST_DIR}/ShaderProgram.cpp - ${CMAKE_CURRENT_LIST_DIR}/FrameBuffer.cpp ${CMAKE_CURRENT_LIST_DIR}/Texture.cpp ${CMAKE_CURRENT_LIST_DIR}/CubeMap.cpp ${CMAKE_CURRENT_LIST_DIR}/Utils.cpp + + ${CMAKE_CURRENT_LIST_DIR}/FrameBuffers/SceneFrameBuffer.cpp + ${CMAKE_CURRENT_LIST_DIR}/FrameBuffers/DepthFrameBuffer.cpp ) target_include_directories( diff --git a/src/Rendering/FrameBuffer.cpp b/src/Rendering/FrameBuffer.cpp deleted file mode 100644 index cc376ec1..00000000 --- a/src/Rendering/FrameBuffer.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include - -namespace shkyera { - -FrameBuffer::FrameBuffer() - : _width(0), _height(0), - _textureColorBuffer(GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE) { - setupFramebuffer(); -} - -FrameBuffer::~FrameBuffer() { - glDeleteFramebuffers(1, &_fbo); - glDeleteRenderbuffers(1, &_rbo); -} - -void FrameBuffer::bind() { - glBindFramebuffer(GL_FRAMEBUFFER, _fbo); - glViewport(0, 0, static_cast(_width), static_cast(_height)); -} - -void FrameBuffer::unbind() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -void FrameBuffer::clear() { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void FrameBuffer::setSize(uint32_t width, uint32_t height) { - if (width == _width && height == _height) return; - - _width = width; - _height = height; - - // Resize texture and renderbuffer - _textureColorBuffer.setData(GL_RGB, _width, _height, GL_RGB, GL_UNSIGNED_BYTE); - glBindRenderbuffer(GL_RENDERBUFFER, _rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); -} - -void FrameBuffer::setupFramebuffer() { - glGenFramebuffers(1, &_fbo); - glBindFramebuffer(GL_FRAMEBUFFER, _fbo); - - // Attach texture as the color buffer - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureColorBuffer.getID(), 0); - - // Attach a renderbuffer for depth and stencil - glGenRenderbuffers(1, &_rbo); - glBindRenderbuffer(GL_RENDERBUFFER, _rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -} diff --git a/src/Rendering/FrameBuffer.hpp b/src/Rendering/FrameBuffer.hpp deleted file mode 100644 index 868404c5..00000000 --- a/src/Rendering/FrameBuffer.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace shkyera { - -class FrameBuffer { -public: - FrameBuffer(); - ~FrameBuffer(); - - void bind(); - void unbind(); - void clear(); - - void setSize(uint32_t width, uint32_t height); - const Texture& getTexture() const { return _textureColorBuffer; } - -private: - void setupFramebuffer(); - - GLuint _fbo, _rbo; - uint32_t _width, _height; - Texture _textureColorBuffer; -}; - - -} diff --git a/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp b/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp new file mode 100644 index 00000000..94e2ad89 --- /dev/null +++ b/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp @@ -0,0 +1,111 @@ +#include + +#include + +namespace shkyera { + +DepthFrameBuffer::DepthFrameBuffer(GLenum minFilter, GLenum magFilter, + GLenum wrapS, GLenum wrapT) + : _width(2048), _height(2048), + _textureDepthBuffer(minFilter, magFilter, wrapS, wrapT) { + setupFramebuffer(); +} + +DepthFrameBuffer::DepthFrameBuffer(DepthFrameBuffer&& other) noexcept +{ + _fbo = other._fbo; + _rbo = other._rbo; + _textureDepthBuffer = std::move(other._textureDepthBuffer); + + other._fbo = 0; + other._rbo = 0; +} + +DepthFrameBuffer& DepthFrameBuffer::operator=(DepthFrameBuffer&& other) noexcept +{ + if(this != &other) + { + if(_fbo != 0) + { + glDeleteFramebuffers(1, &_fbo); + } + if(_rbo != 0) + { + glDeleteRenderbuffers(1, &_rbo); + } + + _fbo = other._fbo; + _rbo = other._rbo; + _textureDepthBuffer = std::move(other._textureDepthBuffer); + + other._fbo = 0; + other._rbo = 0; + } + + return *this; +} + +DepthFrameBuffer::~DepthFrameBuffer() { + if(_fbo != 0) + { + glDeleteFramebuffers(1, &_fbo); + } + if(_rbo != 0) + { + glDeleteRenderbuffers(1, &_rbo); + } +} + +void DepthFrameBuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + glViewport(0, 0, static_cast(_width), static_cast(_height)); +} + +void DepthFrameBuffer::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void DepthFrameBuffer::clear() { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void DepthFrameBuffer::setSize(uint32_t width, uint32_t height) { + if (width == _width && height == _height) return; + + _width = width; + _height = height; + + _textureDepthBuffer.setData(GL_R16F, _width, _height, GL_RED, GL_FLOAT); + + glBindRenderbuffer(GL_RENDERBUFFER, _rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); +} + +void DepthFrameBuffer::setupFramebuffer() { + glGenFramebuffers(1, &_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureDepthBuffer.getID(), 0); + + glGenRenderbuffers(1, &_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, _rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo); + + glBindTexture(GL_TEXTURE_2D, _textureDepthBuffer.getID()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + GLfloat borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + // Verify the framebuffer completeness + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cout << "ERROR::FRAMEBUFFER:: Depth Frame Buffer is not complete!" << std::endl; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +} diff --git a/src/Rendering/FrameBuffers/DepthFrameBuffer.hpp b/src/Rendering/FrameBuffers/DepthFrameBuffer.hpp new file mode 100644 index 00000000..34230043 --- /dev/null +++ b/src/Rendering/FrameBuffers/DepthFrameBuffer.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +namespace shkyera { + +class DepthFrameBuffer { +public: + DepthFrameBuffer(GLenum minFilter = GL_LINEAR, GLenum magFilter = GL_LINEAR, + GLenum wrapS = GL_CLAMP_TO_EDGE, GLenum wrapT = GL_CLAMP_TO_EDGE); + ~DepthFrameBuffer(); + + DepthFrameBuffer(const DepthFrameBuffer& other) = delete; + DepthFrameBuffer& operator=(const DepthFrameBuffer& other) = delete; + + DepthFrameBuffer(DepthFrameBuffer&& other) noexcept; + DepthFrameBuffer& operator=(DepthFrameBuffer&& other) noexcept; + + void bind(); + void unbind(); + void clear(); + + void setSize(uint32_t width, uint32_t height); + const Texture& getTexture() const { return _textureDepthBuffer; } + +private: + void setupFramebuffer(); + + int _width, _height; + GLuint _fbo, _rbo; + Texture _textureDepthBuffer; +}; + + +} diff --git a/src/Rendering/FrameBuffers/SceneFrameBuffer.cpp b/src/Rendering/FrameBuffers/SceneFrameBuffer.cpp new file mode 100644 index 00000000..d207c496 --- /dev/null +++ b/src/Rendering/FrameBuffers/SceneFrameBuffer.cpp @@ -0,0 +1,108 @@ +#include + +#include + +namespace shkyera { + +SceneFrameBuffer::SceneFrameBuffer(GLenum minFilter, GLenum magFilter, + GLenum wrapS, GLenum wrapT) + : _width(0), _height(0), + _textureColorBuffer(minFilter, magFilter, wrapS, wrapT) { + setupFramebuffer(); + setSize(256, 256); +} + +SceneFrameBuffer::SceneFrameBuffer(SceneFrameBuffer&& other) noexcept +{ + _fbo = other._fbo; + _rbo = other._rbo; + _textureColorBuffer = std::move(other._textureColorBuffer); + + other._fbo = 0; + other._rbo = 0; + other._textureColorBuffer = Texture{}; +} + +SceneFrameBuffer& SceneFrameBuffer::operator=(SceneFrameBuffer&& other) noexcept +{ + if(this != &other) + { + if(_fbo != 0) + { + glDeleteFramebuffers(1, &_fbo); + } + if(_rbo != 0) + { + glDeleteRenderbuffers(1, &_rbo); + } + + _fbo = other._fbo; + _rbo = other._rbo; + _textureColorBuffer = std::move(other._textureColorBuffer); + + other._fbo = 0; + other._rbo = 0; + other._textureColorBuffer = Texture{}; + } + + return *this; +} + +SceneFrameBuffer::~SceneFrameBuffer() { + if(_fbo != 0) + { + glDeleteFramebuffers(1, &_fbo); + } + if(_rbo != 0) + { + glDeleteRenderbuffers(1, &_rbo); + } +} + +void SceneFrameBuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + glViewport(0, 0, static_cast(_width), static_cast(_height)); +} + +void SceneFrameBuffer::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void SceneFrameBuffer::clear() { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void SceneFrameBuffer::setSize(uint32_t width, uint32_t height) { + if (width == _width && height == _height) return; + + _width = width; + _height = height; + + // Resize texture and renderbuffer + _textureColorBuffer.setData(GL_RGB, _width, _height, GL_RGB, GL_UNSIGNED_BYTE); + glBindRenderbuffer(GL_RENDERBUFFER, _rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); +} + +void SceneFrameBuffer::setupFramebuffer() { + glGenFramebuffers(1, &_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + + // Attach texture as the color buffer + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureColorBuffer.getID(), 0); + + // Attach a renderbuffer for depth and stencil + glGenRenderbuffers(1, &_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, _rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cout << "ERROR::FRAMEBUFFER:: Scene Frame Buffer is not complete!" << std::endl; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +} diff --git a/src/Rendering/FrameBuffers/SceneFrameBuffer.hpp b/src/Rendering/FrameBuffers/SceneFrameBuffer.hpp new file mode 100644 index 00000000..9f9f39d9 --- /dev/null +++ b/src/Rendering/FrameBuffers/SceneFrameBuffer.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +namespace shkyera { + +class SceneFrameBuffer { +public: + SceneFrameBuffer(GLenum minFilter = GL_LINEAR, GLenum magFilter = GL_LINEAR, + GLenum wrapS = GL_CLAMP_TO_EDGE, GLenum wrapT = GL_CLAMP_TO_EDGE); + ~SceneFrameBuffer(); + + SceneFrameBuffer(const SceneFrameBuffer& other) = delete; + SceneFrameBuffer& operator=(const SceneFrameBuffer& other) = delete; + + SceneFrameBuffer(SceneFrameBuffer&& other) noexcept; + SceneFrameBuffer& operator=(SceneFrameBuffer&& other) noexcept; + + void bind(); + void unbind(); + void clear(); + + void setSize(uint32_t width, uint32_t height); + const Texture& getTexture() const { return _textureColorBuffer; } + +private: + void setupFramebuffer(); + + int _width, _height; + GLuint _fbo, _rbo; + Texture _textureColorBuffer; +}; + + +} diff --git a/src/Rendering/Texture.cpp b/src/Rendering/Texture.cpp index 9063c1c9..a9e064cc 100644 --- a/src/Rendering/Texture.cpp +++ b/src/Rendering/Texture.cpp @@ -24,10 +24,32 @@ Texture::Texture(const std::string& path) : Texture() loadImage(image); } +Texture::Texture(Texture&& other) noexcept +{ + _textureID = other._textureID; + other._textureID = 0; +} + +Texture& Texture::operator=(Texture&& other) noexcept +{ + if(this != &other) + { + if(_textureID) + { + glDeleteTextures(1, &_textureID); + } + _textureID = other._textureID; + other._textureID = 0; + } + + return *this; +} + Texture::~Texture() { - if (_textureID) { + + if (_textureID) + { glDeleteTextures(1, &_textureID); - _textureID = 0; } } diff --git a/src/Rendering/Texture.hpp b/src/Rendering/Texture.hpp index 29a29429..316c5077 100644 --- a/src/Rendering/Texture.hpp +++ b/src/Rendering/Texture.hpp @@ -18,6 +18,9 @@ class Texture : public Asset { Texture(const Texture& other) = delete; Texture& operator=(const Texture& other) = delete; + Texture(Texture&& other) noexcept; + Texture& operator=(Texture&& other) noexcept; + // Bind and unbind texture void bind() const; void unbind() const; diff --git a/src/Rendering/Utils.hpp b/src/Rendering/Utils.hpp index b27a5516..7bf50de4 100644 --- a/src/Rendering/Utils.hpp +++ b/src/Rendering/Utils.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace shkyera::utils { @@ -17,7 +17,7 @@ std::pair Uniform(const std::string& name, const ValueTy template void applyShaderToFrameBuffer( - FrameBuffer& frameBuffer, + SceneFrameBuffer& frameBuffer, ShaderProgram& shaderProgram, std::vector> textures, // List of texture bindings with texture unit indices Uniforms... uniforms) // Parameter pack for uniform values @@ -44,4 +44,4 @@ void applyShaderToFrameBuffer( frameBuffer.unbind(); } -} \ No newline at end of file +} diff --git a/src/Runtime/Runtime.hpp b/src/Runtime/Runtime.hpp index 9675ff0e..a95e0406 100644 --- a/src/Runtime/Runtime.hpp +++ b/src/Runtime/Runtime.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include namespace shkyera { diff --git a/src/Systems/RenderingSystem.cpp b/src/Systems/RenderingSystem.cpp index 8161b272..a164d4f5 100644 --- a/src/Systems/RenderingSystem.cpp +++ b/src/Systems/RenderingSystem.cpp @@ -51,6 +51,12 @@ RenderingSystem::RenderingSystem(std::shared_ptr registry) _skyboxShaderProgram.attachShader(skyboxVertexShader); _skyboxShaderProgram.attachShader(cubeMapFragmentShader); _skyboxShaderProgram.link(); + + const auto& shadowMapVertexShader = AssetManager::getInstance().getAsset("resources/shaders/vertex/shadowmap.glsl", Shader::Type::Vertex); + const auto& depthFragmentShader = AssetManager::getInstance().getAsset("resources/shaders/fragment/depth.glsl", Shader::Type::Fragment); + _shadowMapShaderProgram.attachShader(shadowMapVertexShader); + _shadowMapShaderProgram.attachShader(depthFragmentShader); + _shadowMapShaderProgram.link(); } void RenderingSystem::setSize(uint32_t width, uint32_t height) @@ -112,7 +118,7 @@ void RenderingSystem::renderOutline(const std::unordered_set& entities) { "silhouetteTexture", &_silhouetteFrameBuffer.getTexture() } }, utils::Uniform("horizontal", true), - utils::Uniform("kernelSize", 5) + utils::Uniform("kernelSize", 3) ); utils::applyShaderToFrameBuffer( @@ -122,7 +128,7 @@ void RenderingSystem::renderOutline(const std::unordered_set& entities) { "silhouetteTexture", &_horizontallyDilatedFrameBuffer.getTexture() } }, utils::Uniform("horizontal", false), - utils::Uniform("kernelSize", 5) + utils::Uniform("kernelSize", 3) ); utils::applyShaderToFrameBuffer( @@ -148,9 +154,64 @@ void RenderingSystem::renderModels() { glEnable(GL_DEPTH_TEST); + const auto& cameraTransform = _registry->getComponent(_registry->getCamera()); + + // ********* Rendering the shadow maps ********* + std::unordered_set directionalLightEntities; + for(const auto& [entity, directionalLightComponent] : _registry->getComponentSet()) { + directionalLightEntities.insert(entity); + if(_directionalLightToShadowMaps.find(entity) == _directionalLightToShadowMaps.end()) + { + _directionalLightToShadowMaps.emplace(entity, std::array{}); + } + } + + std::vector entitiesWithFrameBuffersToRemove; + for(const auto& [entity, _buffer] : _directionalLightToShadowMaps) + { + if(directionalLightEntities.find(entity) == directionalLightEntities.end()) + { + entitiesWithFrameBuffersToRemove.push_back(entity); + } + } + + for(const auto& entityToRemove : entitiesWithFrameBuffersToRemove) + { + _directionalLightToShadowMaps.erase(entityToRemove); + } + + for(auto& [lightEntity, lodBuffers] : _directionalLightToShadowMaps) + { + uint8_t levelOfDetail = 0; + for(auto& buffer : lodBuffers) + { + buffer.bind(); + buffer.setSize(2048, 2048); + buffer.clear(); + _shadowMapShaderProgram.use(); + + const auto& directionalLightComponent = _registry->getComponent(lightEntity); + const auto& lightTransform = _registry->getComponent(lightEntity); + const glm::mat4& viewMatrix = directionalLightComponent.getViewMatrix(lightTransform, cameraTransform); + const glm::mat4& projectionMatrix = directionalLightComponent.getProjectionMatrix(levelOfDetail++); + + _shadowMapShaderProgram.setUniform("viewMatrix", viewMatrix); + _shadowMapShaderProgram.setUniform("projectionMatrix", projectionMatrix); + for (const auto& [modelEntity, modelComponent] : _registry->getComponentSet()) { + const auto& transformComponent = _registry->getComponent(modelEntity); + _shadowMapShaderProgram.setUniform("modelMatrix", transformComponent.getTransformMatrix()); + + modelComponent.updateImpl(); + } + + _shadowMapShaderProgram.stopUsing(); + buffer.unbind(); + } + } + + // ********* Rendering the world ********* _renderFrameBuffer.bind(); - const auto& cameraTransform = _registry->getComponent(_registry->getCamera()); const glm::mat4& viewMatrix = _registry->getComponent(_registry->getCamera()).getViewMatrix(cameraTransform); const glm::mat4& projectionMatrix = _registry->getComponent(_registry->getCamera()).getProjectionMatrix(); @@ -177,12 +238,27 @@ void RenderingSystem::renderModels() _modelShaderProgram.setUniform("numPointLights", pointLightIndex); int directionalLightIndex = 0; - for(const auto& [entity, pointLightComponent] : _registry->getComponentSet()) { - const auto& rotationMatrix = _registry->getComponent(entity).getRotationMatrix(); - auto lightDirection = rotationMatrix * glm::vec4{0, 0, 1, 1}; + int textureIndex = 0; + for (const auto& [entity, directionalLightComponent] : _registry->getComponentSet()) { + const auto& transformComponent = _registry->getComponent(entity); + const auto& orientation = transformComponent.getOrientation(); + + const auto& depthFrameBuffers = _directionalLightToShadowMaps.at(entity); + + for(size_t levelOfDetail = 0; levelOfDetail < depthFrameBuffers.size(); levelOfDetail++) + { + depthFrameBuffers[levelOfDetail].getTexture().activate(GL_TEXTURE0 + textureIndex); + const glm::mat4 lightSpaceMatrix = directionalLightComponent.getProjectionMatrix(levelOfDetail) * directionalLightComponent.getViewMatrix(transformComponent, cameraTransform); + + _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].shadowSampler[" + std::to_string(levelOfDetail) + "]", textureIndex); + _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].lightSpaceMatrix[" + std::to_string(levelOfDetail) + "]", lightSpaceMatrix); + ++textureIndex; + } + - _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].direction", glm::vec3{lightDirection}); - _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].color", pointLightComponent.intensity * pointLightComponent.color); + glm::vec3 lightDirection = DirectionalLightComponent::getDirection(transformComponent); + _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].direction", lightDirection); + _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].color", directionalLightComponent.intensity * directionalLightComponent.color); ++directionalLightIndex; } _modelShaderProgram.setUniform("numDirectionalLights", directionalLightIndex); @@ -193,8 +269,7 @@ void RenderingSystem::renderModels() const Material* material = modelComponent.getMaterial(); if (material) { - _modelShaderProgram.setUniform("material.diffuse", material->getDiffuseColor()); - _modelShaderProgram.setUniform("material.specular", material->getSpecularColor()); + _modelShaderProgram.setUniform("material.color", material->getDiffuseColor()); _modelShaderProgram.setUniform("material.shininess", material->getShininess()); } diff --git a/src/Systems/RenderingSystem.hpp b/src/Systems/RenderingSystem.hpp index 7acfa994..ec89bdb8 100644 --- a/src/Systems/RenderingSystem.hpp +++ b/src/Systems/RenderingSystem.hpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include namespace shkyera { @@ -27,15 +28,15 @@ class RenderingSystem { std::shared_ptr _registry; // Main Rendering - FrameBuffer _renderFrameBuffer; + SceneFrameBuffer _renderFrameBuffer; ShaderProgram _modelShaderProgram; ShaderProgram _wireframeShaderProgram; // Rendering Object Outline - FrameBuffer _silhouetteFrameBuffer; - FrameBuffer _horizontallyDilatedFrameBuffer; - FrameBuffer _fullyDilatedFrameBuffer; - FrameBuffer _differenceFrameBuffer; + SceneFrameBuffer _silhouetteFrameBuffer; + SceneFrameBuffer _horizontallyDilatedFrameBuffer; + SceneFrameBuffer _fullyDilatedFrameBuffer; + SceneFrameBuffer _differenceFrameBuffer; ShaderProgram _silhouetteShaderProgram; ShaderProgram _dilateShaderProgram; ShaderProgram _subtractShaderProgram; @@ -44,6 +45,11 @@ class RenderingSystem { // Skybox Rendering CubeMap _skyboxCubeMap; ShaderProgram _skyboxShaderProgram; + + // Light rendering + constexpr static size_t DirectionalLightLOD = 4; + std::unordered_map> _directionalLightToShadowMaps; + ShaderProgram _shadowMapShaderProgram; }; } diff --git a/src/main.cpp b/src/main.cpp index 2807cb3d..e630fc06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ #include #include -void addModel(std::shared_ptr registry, +shkyera::Entity addModel(std::shared_ptr registry, const glm::vec3& position, const std::string& name, std::shared_ptr mesh) { @@ -40,9 +40,11 @@ void addModel(std::shared_ptr registry, registry->getComponent(entity).setMaterial(std::make_shared()); registry->addComponent>(entity); registry->getComponent>(entity).box = mesh->getBoundingBox(); + + return entity; } -void addWireframe(std::shared_ptr registry, +shkyera::Entity addWireframe(std::shared_ptr registry, const glm::vec3& position, const std::string& name, std::shared_ptr wireframe) { @@ -57,6 +59,8 @@ void addWireframe(std::shared_ptr registry, registry->getComponent(entity).setName(name); registry->addComponent(entity); registry->getComponent(entity).setWireframe(wireframe); + + return entity; } void loadScene(std::shared_ptr registry) { @@ -65,6 +69,8 @@ void loadScene(std::shared_ptr registry) { // Camera setup auto camera = registry->getCamera(); registry->addComponent(camera); + registry->getComponent(camera).setPosition({0, 15, 0}); + registry->getComponent(camera).setOrientation({-M_PI_2 + 0.01, 0, 0}); registry->addComponent(camera); // Add Cylinder and its wireframe @@ -76,8 +82,12 @@ void loadScene(std::shared_ptr registry) { addWireframe(registry, {3, 3, 0}, "Cube Wireframe", std::shared_ptr(Wireframe::Factory::createCube())); // Add Sphere and its wireframe - addModel(registry, {3, 0, 3}, "Sphere", std::shared_ptr(Mesh::Factory::createSphere())); - addWireframe(registry, {3, 3, 3}, "Sphere Wireframe", std::shared_ptr(Wireframe::Factory::createSphere())); + addModel(registry, {3, 6, 3}, "Sphere", std::shared_ptr(Mesh::Factory::createSphere())); + addWireframe(registry, {3, 9, 3}, "Sphere Wireframe", std::shared_ptr(Wireframe::Factory::createSphere())); + + // Add World Plane + auto worldPlane = addModel(registry, {0, -2, 0}, "Plane", std::shared_ptr(Mesh::Factory::createPlane())); + registry->getComponent(worldPlane).setScale({15, 1, 15}); // Add Point Light auto pointLight = registry->addEntity(); @@ -90,11 +100,24 @@ void loadScene(std::shared_ptr registry) { // Add Skybox auto sky = registry->addEntity(); registry->addComponent(sky); + registry->getComponent(sky).setOrientation({-M_PI_2 + 0.1, -0.1, 0}); registry->addComponent(sky); - registry->getComponent(sky).setName("Sky"); + registry->getComponent(sky).setName("Sun"); registry->addComponent(sky); + registry->getComponent(sky).color = glm::vec3{0.95, 0.92, 0.6}; + registry->getComponent(sky).intensity = 0.4; registry->addComponent(sky); + auto moon = registry->addEntity(); + registry->addComponent(moon); + registry->getComponent(moon).setOrientation({-M_PI_2 / 2, -M_PI_2 / 2, 0}); + registry->addComponent(moon); + registry->getComponent(moon).setName("Moon"); + registry->addComponent(moon); + registry->getComponent(moon).color = glm::vec3{0.85, 0.95, 0.95}; + registry->getComponent(moon).intensity = 0.15; + registry->addComponent(moon); + const auto skyboxUp = AssetManager::getInstance().getAsset("resources/skyboxes/day/py.png"); const auto skyboxDown = AssetManager::getInstance().getAsset("resources/skyboxes/day/ny.png"); const auto skyboxLeft = AssetManager::getInstance().getAsset("resources/skyboxes/day/nx.png"); diff --git a/src/ui/UI.cpp b/src/ui/UI.cpp index 70a89fb0..6edd52b0 100644 --- a/src/ui/UI.cpp +++ b/src/ui/UI.cpp @@ -54,8 +54,8 @@ void UI::initializeImgui() { #elif defined(__APPLE__) // GL 3.2 + GLSL 150 const char* glsl_version = "#version 150"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac From 436aff223b793955a63671faa7cbd320efab76b7 Mon Sep 17 00:00:00 2001 From: Franciszek Szewczyk Date: Sun, 10 Nov 2024 14:51:58 +0100 Subject: [PATCH 2/2] Cascaded Shadow Maps --- resources/shaders/fragment/uber.glsl | 67 +++++++++++++------ resources/shaders/vertex/shadowmap.glsl | 8 +-- src/Components/CameraComponent.hpp | 39 ++++++++--- src/Components/DirectionalLightComponent.hpp | 47 +++++++------ .../FrameBuffers/DepthFrameBuffer.cpp | 10 +-- src/Systems/RenderingSystem.cpp | 22 +++--- src/Systems/RenderingSystem.hpp | 4 +- src/main.cpp | 18 ++--- 8 files changed, 131 insertions(+), 84 deletions(-) diff --git a/resources/shaders/fragment/uber.glsl b/resources/shaders/fragment/uber.glsl index 73db1e4b..146d165b 100644 --- a/resources/shaders/fragment/uber.glsl +++ b/resources/shaders/fragment/uber.glsl @@ -96,34 +96,43 @@ float calculateShadowFactor(int directionalLightIndex, LodBlend lodBlend) { } // Define PCF kernel size and bias for shadow comparison - float bias = 0.001; - float shadowLowLod = 0.0; - float shadowHighLod = 0.0; - int pcfKernelSize = - 2; // You can adjust this value for more or less smoothing - float texelSize = - 1.0 / 2048.0; // Assume a shadow map resolution of 2048x2048 + float bias = 0.01; + int pcfKernelSize = 1; + + // Set texel sizes for low and high LODs + float texelSizeLowLod = + 1.0 / 1024.0; // Low LOD texel size (assumed 1024x1024) + float texelSizeHighLod = + 1.0 * texelSizeLowLod; // High LOD texel size, twice as large - // Loop over the kernel to sample neighboring depths for the low LOD + // Calculate shadow factor for low LOD + float shadowLowLod = 0.0; for (int x = -pcfKernelSize; x <= pcfKernelSize; ++x) { for (int y = -pcfKernelSize; y <= pcfKernelSize; ++y) { - // Sample the shadow map for the low LOD + // Sample shadow map for low LOD float closestDepthLowLod = texture(directionalLights[directionalLightIndex] .shadowSampler[lodBlend.lowLod], - projCoordsLowLod.xy + vec2(x, y) * texelSize) + projCoordsLowLod.xy + vec2(x, y) * texelSizeLowLod) .r; closestDepthLowLod = closestDepthLowLod * 0.5 + 0.5; // Remap to [0, 1] range if (projCoordsLowLod.z - bias > closestDepthLowLod) { shadowLowLod += 1.0; } + } + } + shadowLowLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1)); - // Sample the shadow map for the high LOD + // Calculate shadow factor for high LOD + float shadowHighLod = 0.0; + for (int x = -pcfKernelSize; x <= pcfKernelSize; ++x) { + for (int y = -pcfKernelSize; y <= pcfKernelSize; ++y) { + // Sample shadow map for high LOD float closestDepthHighLod = texture(directionalLights[directionalLightIndex] .shadowSampler[lodBlend.highLod], - projCoordsHighLod.xy + vec2(2 * x, 2 * y) * texelSize) + projCoordsHighLod.xy + vec2(x, y) * texelSizeHighLod) .r; closestDepthHighLod = closestDepthHighLod * 0.5 + 0.5; // Remap to [0, 1] range @@ -132,9 +141,6 @@ float calculateShadowFactor(int directionalLightIndex, LodBlend lodBlend) { } } } - - // Normalize shadow values by the kernel size - shadowLowLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1)); shadowHighLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1)); // Blend the shadow factors between the low and high LOD based on highLodWeight @@ -142,28 +148,47 @@ float calculateShadowFactor(int directionalLightIndex, LodBlend lodBlend) { } LodBlend chooseLightLOD(mat4 lightSpaceMatrix[4], vec3 fragPos) { - // Transform fragment position to light space + const float blendRange = 0.2; // Range near cascade far edge to blend + for (int lod = 0; lod < 4; ++lod) { vec4 fragPosLightSpace = lightSpaceMatrix[lod] * vec4(fragPos, 1.0); vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; + // Check if fragment is inside current LOD's view if (projCoords.x >= 0.0 && projCoords.y >= 0.0 && projCoords.x <= 1.0 && projCoords.y <= 1.0 && projCoords.z < 1.0) { + + // If we're at the last LOD, no blending with a higher LOD if (lod == 3) { return LodBlend(lod, lod, 1.0); - } else { - float distanceToEdgeX = min(projCoords.x, 1.0 - projCoords.x); - float distanceToEdgeY = min(projCoords.y, 1.0 - projCoords.y); + } + + // Transform to next LOD’s light space to calculate depth blending + vec4 fragPosLightSpaceNext = + lightSpaceMatrix[lod + 1] * vec4(fragPos, 1.0); + vec3 projCoordsNext = fragPosLightSpaceNext.xyz / fragPosLightSpaceNext.w; + projCoordsNext = projCoordsNext * 0.5 + 0.5; - float distanceToEdge = min(distanceToEdgeX, distanceToEdgeY); + // Verify that fragment is also within next LOD's frustum + if (projCoordsNext.x >= 0.0 && projCoordsNext.y >= 0.0 && + projCoordsNext.x <= 1.0 && projCoordsNext.y <= 1.0 && + projCoordsNext.z < 1.0) { + + // Compute blending weight based on fragment's depth in current LOD + float depthInCurrentLOD = projCoords.z; + float highLodWeight = + smoothstep(1.0 - blendRange, 1.0, depthInCurrentLOD); - float highLodWeight = distanceToEdge / 0.5; return LodBlend(lod, lod + 1, highLodWeight); } + + // If fragment not inside next LOD, return current LOD without blending + return LodBlend(lod, lod, 1.0); } } + // Default return if fragment is outside all cascades return LodBlend(-1, -1, 0.0); } diff --git a/resources/shaders/vertex/shadowmap.glsl b/resources/shaders/vertex/shadowmap.glsl index 21e7821b..21ddc47b 100644 --- a/resources/shaders/vertex/shadowmap.glsl +++ b/resources/shaders/vertex/shadowmap.glsl @@ -2,16 +2,14 @@ layout(location = 0) in vec3 aPos; // Vertex position -uniform mat4 modelMatrix; // Model matrix -uniform mat4 viewMatrix; // View matrix -uniform mat4 projectionMatrix; // Projection matrix +uniform mat4 lightSpaceMatrix; // Model matrix +uniform mat4 modelMatrix; // Projection matrix out float FragDepth; // Output the depth value to the fragment shader void main() { // Transform vertex position to world space, then to light space - vec4 lightSpacePosition = - projectionMatrix * viewMatrix * modelMatrix * vec4(aPos, 1.0); + vec4 lightSpacePosition = lightSpaceMatrix * modelMatrix * vec4(aPos, 1.0); // Set the final position gl_Position = lightSpacePosition; diff --git a/src/Components/CameraComponent.hpp b/src/Components/CameraComponent.hpp index ed309898..7db2b940 100644 --- a/src/Components/CameraComponent.hpp +++ b/src/Components/CameraComponent.hpp @@ -14,7 +14,7 @@ class CameraComponent { Orthographic }; - CameraComponent(float fov = 45.0f, float aspectRatio = 16.0f / 9.0f, float nearPlane = 0.1f, float farPlane = 10000.0f, ProjectionType projectionType = ProjectionType::Perspective) + CameraComponent(float fov = 45.0f, float aspectRatio = 16.0f / 9.0f, float nearPlane = 0.01f, float farPlane = 2000.0f, ProjectionType projectionType = ProjectionType::Perspective) : fov(fov), aspectRatio(aspectRatio), nearPlane(nearPlane), farPlane(farPlane), projectionType(projectionType) {} float fov; @@ -40,14 +40,7 @@ class CameraComponent { } glm::mat4 getProjectionMatrix() const { - if (projectionType == ProjectionType::Perspective) { - return glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane); - } else { - float orthoSize = 10.0f; // Can be configurable - float halfWidth = orthoSize * aspectRatio * 0.5f; - float halfHeight = orthoSize * 0.5f; - return glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane); - } + return getProjectionMatrix(nearPlane, farPlane); } Ray getRayAt(const TransformComponent& transformComponent, float x, float y) const { @@ -74,6 +67,34 @@ class CameraComponent { return { rayOrigin, rayDirection }; } + std::vector getFrustumCornersWorldSpace(float localNearPlane, float localFarPlane, const TransformComponent& transformComponent) const { + glm::mat4 invViewProj = glm::inverse(getProjectionMatrix(localNearPlane, localFarPlane) * getViewMatrix(transformComponent)); + + std::vector frustumCorners; + for (int x = -1; x <= 1; x += 2) { + for (int y = -1; y <= 1; y += 2) { + for (int z = -1; z <= 1; z += 2) { + glm::vec4 corner = invViewProj * glm::vec4(x, y, z, 1.0f); + corner /= corner.w; + frustumCorners.push_back(glm::vec3(corner)); + } + } + } + return frustumCorners; + } + + private: + glm::mat4 getProjectionMatrix(float localNearPlane, float localFarPlane) const { + if (projectionType == ProjectionType::Perspective) { + return glm::perspective(glm::radians(fov), aspectRatio, localNearPlane, localFarPlane); + } else { + float orthoSize = 10.0f; // Can be configurable + float halfWidth = orthoSize * aspectRatio * 0.5f; + float halfHeight = orthoSize * 0.5f; + return glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, localNearPlane, localFarPlane); + } + } + }; } // namespace shkyera diff --git a/src/Components/DirectionalLightComponent.hpp b/src/Components/DirectionalLightComponent.hpp index 945efa3a..1ba21a81 100644 --- a/src/Components/DirectionalLightComponent.hpp +++ b/src/Components/DirectionalLightComponent.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace shkyera { @@ -15,33 +16,37 @@ class DirectionalLightComponent : public BaseComponent CascadePlanes = {0.01, 2.0, 5.0, 10.0, 100.0}; + + glm::mat4 getLightSpaceMatrix( + const TransformComponent& lightTransformComponent, + const TransformComponent& cameraTransformComponent, + const CameraComponent& cameraComponent, + uint8_t levelOfDetail) const + { + auto frustumCorners = cameraComponent.getFrustumCornersWorldSpace(CascadePlanes[levelOfDetail], CascadePlanes[levelOfDetail + 1], cameraTransformComponent); - glm::vec3 position = cameraTransformComponent.getPosition() - 100.0f * front; + glm::vec3 front = getDirection(lightTransformComponent); + glm::vec3 center = glm::vec3(0.0f); - glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 right = glm::normalize(glm::cross(front, up)); - up = -glm::normalize(glm::cross(right, front)); + for (const auto& corner : frustumCorners) + center += corner; + center /= frustumCorners.size(); - return glm::lookAt(position, position + front, up); - } + glm::mat4 lightView = glm::lookAt(center + front * 50.0f, center, glm::vec3(0.0f, 1.0f, 0.0f)); - glm::mat4 getProjectionMatrix(uint8_t levelOfDetail) const { - constexpr float NearPlane = 0.1f; - constexpr float FarPlane = 1000.0f; - constexpr float DefaultHalfSize = 10.0f; - constexpr float levelOfDetailExpansion = 2.0f; + glm::vec3 min = glm::vec3(std::numeric_limits::max()); + glm::vec3 max = glm::vec3(std::numeric_limits::lowest()); - float halfSize = powf(levelOfDetailExpansion, levelOfDetail) * DefaultHalfSize; + for (const auto& corner : frustumCorners) { + glm::vec3 cornerInLightSpace = glm::vec3(lightView * glm::vec4(corner, 1.0f)); + min = glm::min(min, cornerInLightSpace); + max = glm::max(max, cornerInLightSpace); + } - return glm::ortho(-halfSize, halfSize, -halfSize, halfSize, NearPlane, FarPlane); + glm::mat4 lightProjection = glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 100.0f); + return lightProjection * lightView; } static glm::vec3 getDirection(const TransformComponent& lightTransformComponent) diff --git a/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp b/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp index 94e2ad89..e79dd48f 100644 --- a/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp +++ b/src/Rendering/FrameBuffers/DepthFrameBuffer.cpp @@ -76,7 +76,7 @@ void DepthFrameBuffer::setSize(uint32_t width, uint32_t height) { _width = width; _height = height; - _textureDepthBuffer.setData(GL_R16F, _width, _height, GL_RED, GL_FLOAT); + _textureDepthBuffer.setData(GL_R32F, _width, _height, GL_RED, GL_FLOAT); glBindRenderbuffer(GL_RENDERBUFFER, _rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height); @@ -94,13 +94,9 @@ void DepthFrameBuffer::setupFramebuffer() { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _rbo); glBindTexture(GL_TEXTURE_2D, _textureDepthBuffer.getID()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLfloat borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); - - // Verify the framebuffer completeness if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { std::cout << "ERROR::FRAMEBUFFER:: Depth Frame Buffer is not complete!" << std::endl; } diff --git a/src/Systems/RenderingSystem.cpp b/src/Systems/RenderingSystem.cpp index a164d4f5..9c80f9b8 100644 --- a/src/Systems/RenderingSystem.cpp +++ b/src/Systems/RenderingSystem.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -155,6 +156,7 @@ void RenderingSystem::renderModels() glEnable(GL_DEPTH_TEST); const auto& cameraTransform = _registry->getComponent(_registry->getCamera()); + const auto& cameraComponent = _registry->getComponent(_registry->getCamera()); // ********* Rendering the shadow maps ********* std::unordered_set directionalLightEntities; @@ -162,7 +164,7 @@ void RenderingSystem::renderModels() directionalLightEntities.insert(entity); if(_directionalLightToShadowMaps.find(entity) == _directionalLightToShadowMaps.end()) { - _directionalLightToShadowMaps.emplace(entity, std::array{}); + _directionalLightToShadowMaps.emplace(entity, std::vector(DirectionalLightComponent::LevelsOfDetail)); } } @@ -183,20 +185,19 @@ void RenderingSystem::renderModels() for(auto& [lightEntity, lodBuffers] : _directionalLightToShadowMaps) { uint8_t levelOfDetail = 0; + lodBuffers.resize(DirectionalLightComponent::LevelsOfDetail); for(auto& buffer : lodBuffers) { buffer.bind(); - buffer.setSize(2048, 2048); + buffer.setSize(1600, 1600); buffer.clear(); _shadowMapShaderProgram.use(); const auto& directionalLightComponent = _registry->getComponent(lightEntity); const auto& lightTransform = _registry->getComponent(lightEntity); - const glm::mat4& viewMatrix = directionalLightComponent.getViewMatrix(lightTransform, cameraTransform); - const glm::mat4& projectionMatrix = directionalLightComponent.getProjectionMatrix(levelOfDetail++); + const glm::mat4& lightSpaceMatrix = directionalLightComponent.getLightSpaceMatrix(lightTransform, cameraTransform, cameraComponent, levelOfDetail); - _shadowMapShaderProgram.setUniform("viewMatrix", viewMatrix); - _shadowMapShaderProgram.setUniform("projectionMatrix", projectionMatrix); + _shadowMapShaderProgram.setUniform("lightSpaceMatrix", lightSpaceMatrix); for (const auto& [modelEntity, modelComponent] : _registry->getComponentSet()) { const auto& transformComponent = _registry->getComponent(modelEntity); _shadowMapShaderProgram.setUniform("modelMatrix", transformComponent.getTransformMatrix()); @@ -206,6 +207,7 @@ void RenderingSystem::renderModels() _shadowMapShaderProgram.stopUsing(); buffer.unbind(); + levelOfDetail++; } } @@ -240,15 +242,15 @@ void RenderingSystem::renderModels() int directionalLightIndex = 0; int textureIndex = 0; for (const auto& [entity, directionalLightComponent] : _registry->getComponentSet()) { - const auto& transformComponent = _registry->getComponent(entity); - const auto& orientation = transformComponent.getOrientation(); + const auto& lightTransform = _registry->getComponent(entity); + const auto& orientation = lightTransform.getOrientation(); const auto& depthFrameBuffers = _directionalLightToShadowMaps.at(entity); for(size_t levelOfDetail = 0; levelOfDetail < depthFrameBuffers.size(); levelOfDetail++) { depthFrameBuffers[levelOfDetail].getTexture().activate(GL_TEXTURE0 + textureIndex); - const glm::mat4 lightSpaceMatrix = directionalLightComponent.getProjectionMatrix(levelOfDetail) * directionalLightComponent.getViewMatrix(transformComponent, cameraTransform); + const glm::mat4 lightSpaceMatrix = directionalLightComponent.getLightSpaceMatrix(lightTransform, cameraTransform, cameraComponent, levelOfDetail); _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].shadowSampler[" + std::to_string(levelOfDetail) + "]", textureIndex); _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].lightSpaceMatrix[" + std::to_string(levelOfDetail) + "]", lightSpaceMatrix); @@ -256,7 +258,7 @@ void RenderingSystem::renderModels() } - glm::vec3 lightDirection = DirectionalLightComponent::getDirection(transformComponent); + glm::vec3 lightDirection = DirectionalLightComponent::getDirection(lightTransform); _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].direction", lightDirection); _modelShaderProgram.setUniform("directionalLights[" + std::to_string(directionalLightIndex) + "].color", directionalLightComponent.intensity * directionalLightComponent.color); ++directionalLightIndex; diff --git a/src/Systems/RenderingSystem.hpp b/src/Systems/RenderingSystem.hpp index ec89bdb8..297ff01f 100644 --- a/src/Systems/RenderingSystem.hpp +++ b/src/Systems/RenderingSystem.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -47,8 +48,7 @@ class RenderingSystem { ShaderProgram _skyboxShaderProgram; // Light rendering - constexpr static size_t DirectionalLightLOD = 4; - std::unordered_map> _directionalLightToShadowMaps; + std::unordered_map> _directionalLightToShadowMaps; ShaderProgram _shadowMapShaderProgram; }; diff --git a/src/main.cpp b/src/main.cpp index e630fc06..56c643cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,15 +108,15 @@ void loadScene(std::shared_ptr registry) { registry->getComponent(sky).intensity = 0.4; registry->addComponent(sky); - auto moon = registry->addEntity(); - registry->addComponent(moon); - registry->getComponent(moon).setOrientation({-M_PI_2 / 2, -M_PI_2 / 2, 0}); - registry->addComponent(moon); - registry->getComponent(moon).setName("Moon"); - registry->addComponent(moon); - registry->getComponent(moon).color = glm::vec3{0.85, 0.95, 0.95}; - registry->getComponent(moon).intensity = 0.15; - registry->addComponent(moon); + // auto moon = registry->addEntity(); + // registry->addComponent(moon); + // registry->getComponent(moon).setOrientation({-M_PI_2 / 2, -M_PI_2 / 2, 0}); + // registry->addComponent(moon); + // registry->getComponent(moon).setName("Moon"); + // registry->addComponent(moon); + // registry->getComponent(moon).color = glm::vec3{0.85, 0.95, 0.95}; + // registry->getComponent(moon).intensity = 0.15; + // registry->addComponent(moon); const auto skyboxUp = AssetManager::getInstance().getAsset("resources/skyboxes/day/py.png"); const auto skyboxDown = AssetManager::getInstance().getAsset("resources/skyboxes/day/ny.png");