From cec970c3053aec16e636a5adf5708bdef235fbfb Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Thu, 7 Nov 2024 00:46:09 +0000 Subject: [PATCH 1/9] feat(rendering): reserve space for point shadows in shadow atlas --- .../render/shadow_atlas/shadow_atlas.hpp | 9 +- engine/src/render/shadow_atlas/plugin.cpp | 112 +++++++++++------- .../src/render/shadow_atlas/shadow_atlas.cpp | 14 ++- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp index 942b3ea929..b78f20f436 100644 --- a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp +++ b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp @@ -41,9 +41,12 @@ namespace cubos::engine /// @brief Whether the shadow atlas texture has already been cleared this frame. bool cleared = false; - /// @brief Stores shadow maps for each shadow caster component. + /// @brief Stores shadow maps for each spot shadow caster component. core::gl::Texture2D atlas{nullptr}; + /// @brief Stores shadow maps for each point shadow caster component. + core::gl::Texture2DArray cubeAtlas{nullptr}; + /// @brief Slot for a shadow map in the shadow atlas. struct Slot { @@ -65,6 +68,10 @@ namespace cubos::engine /// in the atlas. std::vector> slots; + /// @brief Stores the sizes, offsets, and caster ids of the shadow maps + /// in the cube atlas. + std::vector> cubeSlots; + /// @brief Maps shadow caster ids to their corresponding slots. std::map> slotsMap; diff --git a/engine/src/render/shadow_atlas/plugin.cpp b/engine/src/render/shadow_atlas/plugin.cpp index 36aec55a31..5f111d1171 100644 --- a/engine/src/render/shadow_atlas/plugin.cpp +++ b/engine/src/render/shadow_atlas/plugin.cpp @@ -3,16 +3,71 @@ #include #include +#include #include +#include #include #include using cubos::core::io::Window; +using cubos::engine::ShadowAtlas; +using cubos::engine::ShadowCaster; + CUBOS_DEFINE_TAG(cubos::engine::createShadowAtlasTag); CUBOS_DEFINE_TAG(cubos::engine::reserveShadowCastersTag); CUBOS_DEFINE_TAG(cubos::engine::drawToShadowAtlasTag); +static void reserveCasterSlot(std::vector>& slots, + std::map>& slotsMap, + ShadowCaster& casterBaseSettings, int& id) +{ + bool foundSlot = false; + casterBaseSettings.id = id++; + for (const auto& slot : slots) + { + if (slot->casterId == -1) + { + slot->casterId = casterBaseSettings.id; + slotsMap[casterBaseSettings.id] = slot; + foundSlot = true; + break; + } + } + if (!foundSlot) + { + // Subdivide largest slot, which is always the first + auto oldSlot = slots.at(0); + auto newSize = oldSlot->size / glm::vec2(2.0F); + + slots.erase(slots.begin()); + oldSlot->size = newSize; + slots.push_back(oldSlot); + + for (int i = 1; i < 4; i++) + { + // Intentional truncation of (i / 2) so that the result is always 0.0 or 1.0 + // NOLINTBEGIN(bugprone-integer-division) + auto newOffset = oldSlot->offset + + glm::vec2(newSize.x * static_cast(i % 2), newSize.y * static_cast(i / 2)); + // NOLINTEND(bugprone-integer-division) + auto newSlot = std::make_shared(newSize, newOffset, -1); + slots.push_back(newSlot); + } + + // Re-attempt + for (const auto& slot : slots) + { + if (slot->casterId == -1) + { + slot->casterId = casterBaseSettings.id; + slotsMap[casterBaseSettings.id] = slot; + break; + } + } + } +} + void cubos::engine::shadowAtlasPlugin(Cubos& cubos) { cubos.depends(shadowsPlugin); @@ -38,60 +93,25 @@ void cubos::engine::shadowAtlasPlugin(Cubos& cubos) cubos.system("reserve space for shadow casters") .tagged(reserveShadowCastersTag) - .call([](ShadowAtlas& atlas, Query casters) { + .call([](ShadowAtlas& atlas, Query spotCasters, Query pointCasters) { atlas.slots.clear(); + atlas.cubeSlots.clear(); atlas.slotsMap.clear(); atlas.slots.push_back( std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); + atlas.cubeSlots.push_back( + std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); int id = 1; - for (auto [caster] : casters) + for (auto [caster] : spotCasters) + { + reserveCasterSlot(atlas.slots, atlas.slotsMap, caster.baseSettings, id); + } + + for (auto [caster] : pointCasters) { - bool foundSlot = false; - caster.baseSettings.id = id++; - for (const auto& slot : atlas.slots) - { - if (slot->casterId == -1) - { - slot->casterId = caster.baseSettings.id; - atlas.slotsMap[caster.baseSettings.id] = slot; - foundSlot = true; - break; - } - } - if (!foundSlot) - { - // Subdivide largest slot, which is always the first - auto oldSlot = atlas.slots.at(0); - auto newSize = oldSlot->size / glm::vec2(2.0F); - - atlas.slots.erase(atlas.slots.begin()); - oldSlot->size = newSize; - atlas.slots.push_back(oldSlot); - - for (int i = 1; i < 4; i++) - { - // Intentional truncation of (i / 2) so that the result is always 0.0 or 1.0 - // NOLINTBEGIN(bugprone-integer-division) - auto newOffset = oldSlot->offset + glm::vec2(newSize.x * static_cast(i % 2), - newSize.y * static_cast(i / 2)); - // NOLINTEND(bugprone-integer-division) - auto newSlot = std::make_shared(newSize, newOffset, -1); - atlas.slots.push_back(newSlot); - } - - // Re-attempt - for (const auto& slot : atlas.slots) - { - if (slot->casterId == -1) - { - slot->casterId = caster.baseSettings.id; - atlas.slotsMap[caster.baseSettings.id] = slot; - break; - } - } - } + reserveCasterSlot(atlas.cubeSlots, atlas.slotsMap, caster.baseSettings, id); } }); } diff --git a/engine/src/render/shadow_atlas/shadow_atlas.cpp b/engine/src/render/shadow_atlas/shadow_atlas.cpp index 87253c77ac..ed79002a33 100644 --- a/engine/src/render/shadow_atlas/shadow_atlas.cpp +++ b/engine/src/render/shadow_atlas/shadow_atlas.cpp @@ -4,6 +4,7 @@ #include +using cubos::core::gl::Texture2DArrayDesc; using cubos::core::gl::Texture2DDesc; using cubos::core::gl::TextureFormat; using cubos::core::gl::Usage; @@ -25,7 +26,7 @@ void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) { mSize = configSize; - // Prepare common texture description. + // Prepare texture description. Texture2DDesc desc{}; desc.width = mSize.x; desc.height = mSize.y; @@ -34,4 +35,15 @@ void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) // Create shadow atlas texture. desc.format = TextureFormat::Depth32; atlas = rd.createTexture2D(desc); + + // Prepare texture array description. + Texture2DArrayDesc cubeDesc{}; + cubeDesc.width = mSize.x; + cubeDesc.height = mSize.y; + cubeDesc.size = 6; + cubeDesc.usage = Usage::Dynamic; + + // Create shadow cube atlas texture. + cubeDesc.format = TextureFormat::Depth32; + cubeAtlas = rd.createTexture2DArray(cubeDesc); } From 5faf59a7aba02e72595b173d386383f987fe3765 Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:06:16 +0000 Subject: [PATCH 2/9] feat(rendering): rasterize point shadow maps --- .../render/shadow_cube_atlas_rasterizer.gs | 23 +++ .../shadow_cube_atlas_rasterizer.gs.meta | 3 + .../render/shadow_cube_atlas_rasterizer.vs | 13 ++ .../shadow_cube_atlas_rasterizer.vs.meta | 3 + .../shadow_atlas_rasterizer.hpp | 3 + .../render/shadow_atlas_rasterizer/plugin.cpp | 138 +++++++++++++++++- 6 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 engine/assets/render/shadow_cube_atlas_rasterizer.gs create mode 100644 engine/assets/render/shadow_cube_atlas_rasterizer.gs.meta create mode 100644 engine/assets/render/shadow_cube_atlas_rasterizer.vs create mode 100644 engine/assets/render/shadow_cube_atlas_rasterizer.vs.meta diff --git a/engine/assets/render/shadow_cube_atlas_rasterizer.gs b/engine/assets/render/shadow_cube_atlas_rasterizer.gs new file mode 100644 index 0000000000..8c71be9ada --- /dev/null +++ b/engine/assets/render/shadow_cube_atlas_rasterizer.gs @@ -0,0 +1,23 @@ +#version 330 core + +layout(triangles) in; +layout(triangle_strip, max_vertices = 18) out; // max_vertices = 3*6 (6 faces in a cube) + +layout(std140) uniform PerScene +{ + mat4 lightViewProj[6]; // 6 faces in a cube +}; + +void main() +{ + for (int j = 0; j < 6; j++) + { + for (int i = 0; i < 3; i++) // triangles have 3 vertices + { + gl_Position = lightViewProj[j] * gl_in[i].gl_Position; + gl_Layer = j; + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/engine/assets/render/shadow_cube_atlas_rasterizer.gs.meta b/engine/assets/render/shadow_cube_atlas_rasterizer.gs.meta new file mode 100644 index 0000000000..ed38810687 --- /dev/null +++ b/engine/assets/render/shadow_cube_atlas_rasterizer.gs.meta @@ -0,0 +1,3 @@ +{ + "id": "e0c5f304-fccf-496e-b181-08a3007f15b0" +} \ No newline at end of file diff --git a/engine/assets/render/shadow_cube_atlas_rasterizer.vs b/engine/assets/render/shadow_cube_atlas_rasterizer.vs new file mode 100644 index 0000000000..328f7f2af9 --- /dev/null +++ b/engine/assets/render/shadow_cube_atlas_rasterizer.vs @@ -0,0 +1,13 @@ +#version 330 core + +in uvec3 position; + +uniform PerMesh +{ + mat4 model; +}; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); +} diff --git a/engine/assets/render/shadow_cube_atlas_rasterizer.vs.meta b/engine/assets/render/shadow_cube_atlas_rasterizer.vs.meta new file mode 100644 index 0000000000..cf45d7020d --- /dev/null +++ b/engine/assets/render/shadow_cube_atlas_rasterizer.vs.meta @@ -0,0 +1,3 @@ +{ + "id": "b9ac4697-c0d3-4e2d-9607-3da50f071d2d" +} \ No newline at end of file diff --git a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp b/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp index 47f8e6577a..0e397867dc 100644 --- a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp +++ b/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp @@ -20,6 +20,9 @@ namespace cubos::engine /// @brief Framebuffer used by the rasterizer to render to the ShadowAtlas. core::gl::Framebuffer framebuffer{nullptr}; + /// @brief Framebuffer used by the rasterizer to render to the cube ShadowAtlas. + core::gl::Framebuffer cubeFramebuffer{nullptr}; + /// @brief Atlas texture in the current framebuffer. core::gl::Texture2D atlas{nullptr}; }; diff --git a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp index 93597df459..ac1eb6dc24 100644 --- a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp +++ b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +34,10 @@ namespace { glm::mat4 lightViewProj; }; + struct PerSceneCube + { + glm::mat4 lightViewProj[6]; + }; // Holds the data sent per mesh to the GPU. struct PerMesh @@ -44,8 +50,11 @@ namespace CUBOS_ANONYMOUS_REFLECT(State); ShaderPipeline pipeline; + ShaderPipeline cubePipeline; ShaderBindingPoint perSceneBP; ShaderBindingPoint perMeshBP; + ShaderBindingPoint perSceneCubeBP; + ShaderBindingPoint perMeshCubeBP; RasterState rasterState; DepthStencilState depthStencilState; @@ -53,14 +62,20 @@ namespace VertexArray vertexArray; ConstantBuffer perSceneCB; + ConstantBuffer perSceneCubeCB; ConstantBuffer perMeshCB; - State(RenderDevice& renderDevice, const ShaderPipeline& pipeline, VertexBuffer vertexBuffer) + State(RenderDevice& renderDevice, const ShaderPipeline& pipeline, const ShaderPipeline& cubePipeline, + VertexBuffer vertexBuffer) : pipeline(pipeline) + , cubePipeline(cubePipeline) { perSceneBP = pipeline->getBindingPoint("PerScene"); perMeshBP = pipeline->getBindingPoint("PerMesh"); - CUBOS_ASSERT(perSceneBP && perMeshBP, "PerScene and PerMesh binding points must exist"); + perSceneCubeBP = cubePipeline->getBindingPoint("PerScene"); + perMeshCubeBP = cubePipeline->getBindingPoint("PerMesh"); + CUBOS_ASSERT(perSceneBP && perMeshBP && perSceneCubeBP && perMeshCubeBP, + "PerScene and PerMesh binding points must exist"); rasterState = renderDevice.createRasterState({ .cullEnabled = true, @@ -86,6 +101,7 @@ namespace vertexArray = renderDevice.createVertexArray(desc); perSceneCB = renderDevice.createConstantBuffer(sizeof(PerScene), nullptr, Usage::Dynamic); + perSceneCubeCB = renderDevice.createConstantBuffer(sizeof(PerSceneCube), nullptr, Usage::Dynamic); perMeshCB = renderDevice.createConstantBuffer(sizeof(PerMesh), nullptr, Usage::Dynamic); } }; @@ -96,6 +112,9 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) static const Asset VertexShader = AnyAsset("46e8da9e-5fe2-486b-85e2-f565c35eaf5e"); static const Asset PixelShader = AnyAsset("efe81cc1-4665-4d30-a7a6-ca5ccaa64aef"); + static const Asset VertexShaderCube = AnyAsset("b9ac4697-c0d3-4e2d-9607-3da50f071d2d"); + static const Asset GeometryShaderCube = AnyAsset("e0c5f304-fccf-496e-b181-08a3007f15b0"); + cubos.depends(windowPlugin); cubos.depends(assetsPlugin); cubos.depends(renderMeshPlugin); @@ -117,14 +136,18 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) auto& rd = window->renderDevice(); auto vs = assets.read(VertexShader)->shaderStage(); auto ps = assets.read(PixelShader)->shaderStage(); - cmds.emplaceResource(rd, rd.createShaderPipeline(vs, ps), pool.vertexBuffer()); + auto vsCube = assets.read(VertexShaderCube)->shaderStage(); + auto gsCube = assets.read(GeometryShaderCube)->shaderStage(); + cmds.emplaceResource(rd, rd.createShaderPipeline(vs, ps), + rd.createShaderPipeline(vsCube, gsCube, ps), pool.vertexBuffer()); }); cubos.system("rasterize to ShadowAtlas") .tagged(drawToShadowAtlasTag) .call([](State& state, const Window& window, const RenderMeshPool& pool, ShadowAtlas& atlas, ShadowAtlasRasterizer& rasterizer, - Query lights, + Query spotLights, + Query pointLights, Query meshes) { auto& rd = window->renderDevice(); @@ -140,6 +163,11 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) desc.depthStencil.setTexture2DTarget(atlas.atlas); rasterizer.framebuffer = rd.createFramebuffer(desc); + FramebufferDesc cubeDesc{}; + cubeDesc.targetCount = 0; + cubeDesc.depthStencil.setTexture2DArrayTarget(atlas.cubeAtlas); + rasterizer.cubeFramebuffer = rd.createFramebuffer(cubeDesc); + CUBOS_INFO("Recreated ShadowAtlasRasterizer's framebuffer"); } @@ -156,10 +184,9 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) if (!atlas.cleared) { rd.clearDepth(1.0F); - atlas.cleared = true; } - for (auto [caster, light, localToWorld] : lights) + for (auto [caster, light, localToWorld] : spotLights) { // Get light viewport auto slot = atlas.slotsMap.at(caster.baseSettings.id); @@ -208,5 +235,104 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) } } } + + // Bind the framebuffer and set the viewport. + rd.setFramebuffer(rasterizer.cubeFramebuffer); + rd.setViewport(0, 0, static_cast(atlas.getSize().x), static_cast(atlas.getSize().y)); + + // Clear + if (!atlas.cleared) + { + rd.clearDepth(1.0F); + atlas.cleared = true; + } + + for (auto [caster, light, localToWorld] : pointLights) + { + // Get light viewport + auto slot = atlas.slotsMap.at(caster.baseSettings.id); + + // Set the viewport. + rd.setViewport(static_cast(slot->offset.x * float(atlas.getSize().x)), + static_cast(slot->offset.y * float(atlas.getSize().y)), + static_cast(slot->size.x * float(atlas.getSize().x)), + static_cast(slot->size.y * float(atlas.getSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(atlas.getSize().x)), + static_cast(slot->offset.y * float(atlas.getSize().y)), + static_cast(slot->size.x * float(atlas.getSize().x)), + static_cast(slot->size.y * float(atlas.getSize().y))); + + // Send the PerScene data to the GPU. + PerSceneCube perScene; + auto proj = glm::perspective(glm::radians(90.0F), + (float(atlas.getSize().x) * slot->size.x) / + (float(atlas.getSize().y) * slot->size.y), + 0.1F, light.range); + + for (int i = 0; i < 6; i++) + { + glm::mat4 view; + switch (i) + { + case 0: + view = glm::inverse(glm::scale(localToWorld.mat, glm::vec3(1.0F / localToWorld.worldScale()))); + break; + case 1: + view = glm::inverse( + glm::scale(glm::rotate(localToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / localToWorld.worldScale()))); + break; + case 2: + view = glm::inverse( + glm::scale(glm::rotate(localToWorld.mat, glm::radians(90.0F), glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / localToWorld.worldScale()))); + break; + case 3: + view = glm::inverse( + glm::scale(glm::rotate(localToWorld.mat, glm::radians(-90.0F), glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / localToWorld.worldScale()))); + break; + case 4: + view = glm::inverse( + glm::scale(glm::rotate(localToWorld.mat, glm::radians(90.0F), glm::vec3(1.0F, 0.0F, 0.0F)), + glm::vec3(1.0F / localToWorld.worldScale()))); + break; + case 5: + view = glm::inverse( + glm::scale(glm::rotate(localToWorld.mat, glm::radians(-90.0F), glm::vec3(1.0F, 0.0F, 0.0F)), + glm::vec3(1.0F / localToWorld.worldScale()))); + break; + default: + view = glm::inverse(glm::scale(localToWorld.mat, glm::vec3(1.0F / localToWorld.worldScale()))); + break; + } + + perScene.lightViewProj[i] = proj * view; + } + state.perSceneCubeCB->fill(&perScene, sizeof(perScene)); + + // Bind the shader, vertex array and uniform buffer. + rd.setShaderPipeline(state.cubePipeline); + rd.setVertexArray(state.vertexArray); + state.perSceneCubeBP->bind(state.perSceneCubeCB); + state.perMeshCubeBP->bind(state.perMeshCB); + + // Iterate over all mesh buckets and issue draw calls. + for (auto [meshEnt, meshLocalToWorld, mesh, grid] : meshes) + { + // Send the PerMesh data to the GPU. + PerMesh perMesh{ + .model = meshLocalToWorld.mat * glm::translate(glm::mat4(1.0F), grid.offset), + }; + state.perMeshCB->fill(&perMesh, sizeof(perMesh)); + + // Iterate over the buckets of the mesh (it may be split over many of them). + for (auto bucket = mesh.firstBucketId; bucket != RenderMeshPool::BucketId::Invalid; + bucket = pool.next(bucket)) + { + rd.drawTriangles(pool.bucketSize() * bucket.inner, pool.vertexCount(bucket)); + } + } + } }); } From b41366c84b016ea5a5eeaeb8f584aac406755f9b Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:03:08 +0000 Subject: [PATCH 3/9] feat(rendering): change deferred shading to render point shadows --- engine/assets/render/deferred_shading.fs | 74 ++++++++++++++- engine/src/render/deferred_shading/plugin.cpp | 92 +++++++++++++++++-- 2 files changed, 158 insertions(+), 8 deletions(-) diff --git a/engine/assets/render/deferred_shading.fs b/engine/assets/render/deferred_shading.fs index 97683d8f4b..40a9fa4898 100644 --- a/engine/assets/render/deferred_shading.fs +++ b/engine/assets/render/deferred_shading.fs @@ -7,6 +7,7 @@ uniform sampler2D normalTexture; uniform sampler2D albedoTexture; uniform sampler2D ssaoTexture; uniform sampler2D shadowAtlasTexture; +uniform sampler2DArray shadowCubeAtlasTexture; uniform sampler2DArray directionalShadowMap; // only one directional light with shadows is supported, for now @@ -32,8 +33,14 @@ struct PointLight { vec4 position; vec4 color; + mat4 matrices[6]; float intensity; float range; + float shadowBias; + float shadowBlurRadius; + vec2 shadowMapOffset; + vec2 shadowMapSize; + float normalOffsetScale; }; struct SpotLight @@ -212,12 +219,77 @@ vec3 directionalLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI, bool drawS vec3 pointLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI) { vec3 toLight = vec3(pointLights[lightI].position) - fragPos; + // Shadows + float shadow = 0.0; + if (pointLights[lightI].shadowMapSize.x > 0.0) + { + // Select cube face + int face = 0; + float rx = -toLight.x; + float ry = -toLight.y; + float rz = -toLight.z; + if (abs(rz) >= abs(rx) && abs(rz) >= abs(ry)) + { + if (rz <= 0) // z- + face = 0; + else // z+ + face = 1; + } + else if (abs(rx) >= abs(ry) && abs(rx) >= abs(rz)) + { + if (rx <= 0) // x- + face = 2; + else // x+ + face = 3; + } + else if (abs(ry) >= abs(rx) && abs(ry) >= abs(rz)) + { + if (ry <= 0) // y- + face = 5; + else // y+ + face = 4; + } + + float normalOffsetScale = pointLights[lightI].normalOffsetScale; + vec3 offsetFragPos = normalOffsetScale > 0.0 ? applyNormalOffset(fragNormal, toLight, fragPos, normalOffsetScale) : fragPos; + vec4 positionLightSpace = pointLights[lightI].matrices[face] * vec4(offsetFragPos, 1.0); + vec3 projCoords = positionLightSpace.xyz / positionLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + vec2 uv = projCoords.xy * pointLights[lightI].shadowMapSize + pointLights[lightI].shadowMapOffset; + float currentDepth = projCoords.z; + float bias = pointLights[lightI].shadowBias / positionLightSpace.w; // make the bias not depend on near/far planes + // PCF + if (pointLights[lightI].shadowBlurRadius <= 0.001f) + { + float pcfDepth = texture(shadowCubeAtlasTexture, vec3(uv.xy, face)).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + } + else + { + vec2 texelSize = vec2(1.0 / 1024.0); // largely arbitrary value, affects blur size + for(int xi = -1; xi <= 1; xi++) + { + for(int yi = -1; yi <= 1; yi++) + { + float x = pointLights[lightI].shadowBlurRadius*float(xi); + float y = pointLights[lightI].shadowBlurRadius*float(yi); + vec2 newUv = uv + vec2(x, y) * texelSize; + float pcfDepth = texture(shadowCubeAtlasTexture, vec3(newUv.xy, face)).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + } + } + shadow /= 9.0; + } + } + + // Lighting float r = length(toLight) / pointLights[lightI].range; if (r < 1) { float attenuation = clamp(1.0 / (1.0 + 25.0 * r * r) * clamp((1 - r) * 5.0, 0, 1), 0, 1); float diffuse = max(dot(fragNormal, vec3(normalize(toLight))), 0); - return attenuation * diffuse * pointLights[lightI].intensity * vec3(pointLights[lightI].color); + return attenuation * diffuse * (1.0 - shadow) * pointLights[lightI].intensity + * vec3(pointLights[lightI].color); } return vec3(0); } diff --git a/engine/src/render/deferred_shading/plugin.cpp b/engine/src/render/deferred_shading/plugin.cpp index 8fed9daf14..08f5c45e49 100644 --- a/engine/src/render/deferred_shading/plugin.cpp +++ b/engine/src/render/deferred_shading/plugin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -68,9 +69,15 @@ namespace { glm::vec4 position; glm::vec4 color; + glm::mat4 matrices[6]; float intensity; float range; - float padding[2]; + float shadowBias; + float shadowBlurRadius; + glm::vec2 shadowMapOffset; + glm::vec2 shadowMapSize; + float normalOffsetScale; + float padding[3]; }; struct PerSpotLight @@ -120,6 +127,7 @@ namespace ShaderBindingPoint albedoBP; ShaderBindingPoint ssaoBP; ShaderBindingPoint shadowAtlasBP; + ShaderBindingPoint shadowCubeAtlasBP; ShaderBindingPoint directionalShadowMapBP; ShaderBindingPoint perSceneBP; ShaderBindingPoint viewportOffsetBP; @@ -138,14 +146,15 @@ namespace albedoBP = pipeline->getBindingPoint("albedoTexture"); ssaoBP = pipeline->getBindingPoint("ssaoTexture"); shadowAtlasBP = pipeline->getBindingPoint("shadowAtlasTexture"); + shadowCubeAtlasBP = pipeline->getBindingPoint("shadowCubeAtlasTexture"); directionalShadowMapBP = pipeline->getBindingPoint("directionalShadowMap"); perSceneBP = pipeline->getBindingPoint("PerScene"); viewportOffsetBP = pipeline->getBindingPoint("viewportOffset"); viewportSizeBP = pipeline->getBindingPoint("viewportSize"); - CUBOS_ASSERT(positionBP && normalBP && albedoBP && ssaoBP && shadowAtlasBP && directionalShadowMapBP && - perSceneBP && viewportOffsetBP && viewportSizeBP, - "positionTexture, normalTexture, albedoTexture, ssaoTexture, shadowAtlasTexture, " - "directionalShadowMap, PerScene, " + CUBOS_ASSERT(positionBP && normalBP && albedoBP && ssaoBP && shadowAtlasBP && shadowCubeAtlasBP && + directionalShadowMapBP && perSceneBP && viewportOffsetBP && viewportSizeBP, + "positionTexture, normalTexture, albedoTexture, ssaoTexture, shadowAtlasTexture" + "shadowCubeAtlasTexture, directionalShadowMap, PerScene, " "viewportOffset and " "viewportSize binding points must exist"); @@ -210,7 +219,7 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) const ShadowAtlas& shadowAtlas, Query> directionalLights, - Query pointLights, + Query> pointLights, Query> spotLights, Query targets, Query cameras) { @@ -340,13 +349,81 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) directionalLightIndex++; } - for (auto [lightLocalToWorld, light] : pointLights) + for (auto [lightLocalToWorld, light, caster] : pointLights) { auto& perLight = perScene.pointLights[perScene.numPointLights++]; perLight.position = lightLocalToWorld.mat * glm::vec4(0.0F, 0.0F, 0.0F, 1.0F); perLight.color = glm::vec4(light.color, 1.0F); perLight.intensity = light.intensity; perLight.range = light.range; + + if (caster.contains()) + { + // Get light viewport + auto slot = shadowAtlas.slotsMap.at(caster.value().baseSettings.id); + + auto lightProj = glm::perspective(glm::radians(90.0F), + (float(shadowAtlas.getSize().x) * slot->size.x) / + (float(shadowAtlas.getSize().y) * slot->size.y), + 0.1F, light.range); + + for (int i = 0; i < 6; i++) + { + glm::mat4 lightView; + switch (i) + { + case 0: + lightView = glm::inverse(glm::scale( + lightLocalToWorld.mat, glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + case 1: + lightView = + glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(180.0F), + glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + case 2: + lightView = + glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(90.0F), + glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + case 3: + lightView = + glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(-90.0F), + glm::vec3(0.0F, 1.0F, 0.0F)), + glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + case 4: + lightView = + glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(90.0F), + glm::vec3(1.0F, 0.0F, 0.0F)), + glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + case 5: + lightView = + glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(-90.0F), + glm::vec3(1.0F, 0.0F, 0.0F)), + glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + default: + lightView = glm::inverse(glm::scale( + lightLocalToWorld.mat, glm::vec3(1.0F / lightLocalToWorld.worldScale()))); + break; + } + + perLight.matrices[i] = lightProj * lightView; + } + perLight.shadowMapOffset = slot->offset; + perLight.shadowMapSize = slot->size; + perLight.shadowBias = caster.value().baseSettings.bias; + perLight.shadowBlurRadius = caster.value().baseSettings.blurRadius; + perLight.normalOffsetScale = caster.value().baseSettings.normalOffsetScale; + } + else + { + perLight.shadowMapSize = glm::vec2(0.0F, 0.0F); + } } for (auto [lightLocalToWorld, light, caster] : spotLights) @@ -407,6 +484,7 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) state.albedoBP->bind(gBuffer.albedo); state.ssaoBP->bind(ssao.blurTexture); state.shadowAtlasBP->bind(shadowAtlas.atlas); + state.shadowCubeAtlasBP->bind(shadowAtlas.cubeAtlas); // directionalShadowMap needs to be bound even if it's null, or else errors may occur on some GPUs state.directionalShadowMapBP->bind(directionalShadowMap); state.directionalShadowMapBP->bind(state.directionalShadowSampler); From fadca4355222bf8473a7bfe55d963956c1854467 Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:03:41 +0000 Subject: [PATCH 4/9] docs(rendering): add point shadows to shadows sample --- engine/samples/render/shadows/assets/main.cubos | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/engine/samples/render/shadows/assets/main.cubos b/engine/samples/render/shadows/assets/main.cubos index 6604a39c4d..24446ba872 100644 --- a/engine/samples/render/shadows/assets/main.cubos +++ b/engine/samples/render/shadows/assets/main.cubos @@ -104,6 +104,23 @@ }, "cubos::engine::SpotShadowCaster": {} }, + "point1": { + "cubos::engine::PointLight": { + "color": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "intensity": 1.0, + "range": 50.0 + }, + "cubos::engine::Position": { + "x": 0.0, + "y": -2.0, + "z": -18.0 + }, + "cubos::engine::PointShadowCaster": {} + }, "sun": { "cubos::engine::DirectionalLight": { "color": { From f5d6100578223bdb9ffef8d678e902a16cc2da4d Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:51:12 +0000 Subject: [PATCH 5/9] docs: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc2b3058a..74c2576f84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the option to use Shadow Normal Offset Bias algorithm (#1308, **@GalaxyCrush**) - UI text element using MSDF for text rendering (#1300, **@mkuritsu**). - Added anti-aliasing using FXAA technique (#1334, **@kuukitenshi**). +- Point light shadows (#1188, **@tomas7770**). ### Changed From bae30ca1da575fd17cd951c391494b0576d830a3 Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:23:52 +0000 Subject: [PATCH 6/9] feat(rendering): allow different resolutions between spot and point shadow atlases --- .../render/shadow_atlas/shadow_atlas.hpp | 17 ++++++++++++-- engine/src/render/deferred_shading/plugin.cpp | 4 ++-- engine/src/render/shadow_atlas/plugin.cpp | 5 +++-- .../src/render/shadow_atlas/shadow_atlas.cpp | 11 ++++++++-- .../render/shadow_atlas_rasterizer/plugin.cpp | 22 +++++++++---------- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp index b78f20f436..1143671877 100644 --- a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp +++ b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp @@ -28,8 +28,12 @@ namespace cubos::engine /// @return Size of the shadow atlas texture, in pixels. glm::uvec2 getSize() const; - /// @brief Recreates the shadow atlas texture. - /// @param rd Render device used to create the texture. + /// @brief Gets the size of the shadow cube atlas texture. + /// @return Size of the shadow cube atlas texture, in pixels. + glm::uvec2 getCubeSize() const; + + /// @brief Recreates the shadow atlas textures. + /// @param rd Render device used to create the textures. void resize(cubos::core::gl::RenderDevice& rd); /// @brief Configured size of the shadow atlas texture, in pixels. @@ -38,6 +42,12 @@ namespace cubos::engine /// actual texture size. glm::uvec2 configSize = {4096, 4096}; + /// @brief Configured size of the cube shadow atlas texture, in pixels. + /// Use this to change the resolution of the cube atlas. Note that the + /// texture isn't immediately resized; use @ref getCubeSize() to get the + /// actual texture size. + glm::uvec2 configCubeSize = {1024, 1024}; + /// @brief Whether the shadow atlas texture has already been cleared this frame. bool cleared = false; @@ -78,5 +88,8 @@ namespace cubos::engine private: /// @brief Size of the shadow atlas texture, in pixels. glm::uvec2 mSize = {0, 0}; + + /// @brief Size of the cube shadow atlas texture, in pixels. + glm::uvec2 mCubeSize = {0, 0}; }; } // namespace cubos::engine diff --git a/engine/src/render/deferred_shading/plugin.cpp b/engine/src/render/deferred_shading/plugin.cpp index 08f5c45e49..45b54950a8 100644 --- a/engine/src/render/deferred_shading/plugin.cpp +++ b/engine/src/render/deferred_shading/plugin.cpp @@ -363,8 +363,8 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) auto slot = shadowAtlas.slotsMap.at(caster.value().baseSettings.id); auto lightProj = glm::perspective(glm::radians(90.0F), - (float(shadowAtlas.getSize().x) * slot->size.x) / - (float(shadowAtlas.getSize().y) * slot->size.y), + (float(shadowAtlas.getCubeSize().x) * slot->size.x) / + (float(shadowAtlas.getCubeSize().y) * slot->size.y), 0.1F, light.range); for (int i = 0; i < 6; i++) diff --git a/engine/src/render/shadow_atlas/plugin.cpp b/engine/src/render/shadow_atlas/plugin.cpp index 5f111d1171..c10509dbeb 100644 --- a/engine/src/render/shadow_atlas/plugin.cpp +++ b/engine/src/render/shadow_atlas/plugin.cpp @@ -80,11 +80,12 @@ void cubos::engine::shadowAtlasPlugin(Cubos& cubos) cubos.tag(drawToShadowAtlasTag).after(reserveShadowCastersTag); cubos.system("create ShadowAtlas").tagged(createShadowAtlasTag).call([](const Window& window, ShadowAtlas& atlas) { - if (atlas.getSize() != atlas.configSize) + if (atlas.getSize() != atlas.configSize || atlas.getCubeSize() != atlas.configCubeSize) { atlas.resize(window->renderDevice()); - CUBOS_INFO("Resized ShadowAtlas to {}x{}", atlas.getSize().x, atlas.getSize().y); + CUBOS_INFO("Resized ShadowAtlas to {}x{} (spot), {}x{} (point)", atlas.getSize().x, atlas.getSize().y, + atlas.getCubeSize().x, atlas.getCubeSize().y); } // New frame, hint that the shadow atlas texture needs to be cleared. diff --git a/engine/src/render/shadow_atlas/shadow_atlas.cpp b/engine/src/render/shadow_atlas/shadow_atlas.cpp index ed79002a33..716553a8c8 100644 --- a/engine/src/render/shadow_atlas/shadow_atlas.cpp +++ b/engine/src/render/shadow_atlas/shadow_atlas.cpp @@ -13,6 +13,7 @@ CUBOS_REFLECT_IMPL(cubos::engine::ShadowAtlas) { return core::ecs::TypeBuilder("cubos::engine::ShadowAtlas") .withField("configSize", &ShadowAtlas::configSize) + .withField("configCubeSize", &ShadowAtlas::configCubeSize) .withField("cleared", &ShadowAtlas::cleared) .build(); } @@ -22,9 +23,15 @@ glm::uvec2 cubos::engine::ShadowAtlas::getSize() const return mSize; } +glm::uvec2 cubos::engine::ShadowAtlas::getCubeSize() const +{ + return mCubeSize; +} + void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) { mSize = configSize; + mCubeSize = configCubeSize; // Prepare texture description. Texture2DDesc desc{}; @@ -38,8 +45,8 @@ void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) // Prepare texture array description. Texture2DArrayDesc cubeDesc{}; - cubeDesc.width = mSize.x; - cubeDesc.height = mSize.y; + cubeDesc.width = mCubeSize.x; + cubeDesc.height = mCubeSize.y; cubeDesc.size = 6; cubeDesc.usage = Usage::Dynamic; diff --git a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp index ac1eb6dc24..cbfcdd6312 100644 --- a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp +++ b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp @@ -238,7 +238,7 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) // Bind the framebuffer and set the viewport. rd.setFramebuffer(rasterizer.cubeFramebuffer); - rd.setViewport(0, 0, static_cast(atlas.getSize().x), static_cast(atlas.getSize().y)); + rd.setViewport(0, 0, static_cast(atlas.getCubeSize().x), static_cast(atlas.getCubeSize().y)); // Clear if (!atlas.cleared) @@ -253,20 +253,20 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) auto slot = atlas.slotsMap.at(caster.baseSettings.id); // Set the viewport. - rd.setViewport(static_cast(slot->offset.x * float(atlas.getSize().x)), - static_cast(slot->offset.y * float(atlas.getSize().y)), - static_cast(slot->size.x * float(atlas.getSize().x)), - static_cast(slot->size.y * float(atlas.getSize().y))); - rd.setScissor(static_cast(slot->offset.x * float(atlas.getSize().x)), - static_cast(slot->offset.y * float(atlas.getSize().y)), - static_cast(slot->size.x * float(atlas.getSize().x)), - static_cast(slot->size.y * float(atlas.getSize().y))); + rd.setViewport(static_cast(slot->offset.x * float(atlas.getCubeSize().x)), + static_cast(slot->offset.y * float(atlas.getCubeSize().y)), + static_cast(slot->size.x * float(atlas.getCubeSize().x)), + static_cast(slot->size.y * float(atlas.getCubeSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(atlas.getCubeSize().x)), + static_cast(slot->offset.y * float(atlas.getCubeSize().y)), + static_cast(slot->size.x * float(atlas.getCubeSize().x)), + static_cast(slot->size.y * float(atlas.getCubeSize().y))); // Send the PerScene data to the GPU. PerSceneCube perScene; auto proj = glm::perspective(glm::radians(90.0F), - (float(atlas.getSize().x) * slot->size.x) / - (float(atlas.getSize().y) * slot->size.y), + (float(atlas.getCubeSize().x) * slot->size.x) / + (float(atlas.getCubeSize().y) * slot->size.y), 0.1F, light.range); for (int i = 0; i < 6; i++) From 207610a68829ca11ea7695682316c73917a4f64b Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Sat, 16 Nov 2024 19:37:24 +0000 Subject: [PATCH 7/9] style: clarify distinction between spot and point shadow atlases --- engine/assets/render/deferred_shading.fs | 12 +-- .../render/shadow_atlas/shadow_atlas.hpp | 58 +++++++------- .../shadow_atlas_rasterizer.hpp | 10 +-- engine/src/render/deferred_shading/plugin.cpp | 36 ++++----- engine/src/render/shadow_atlas/plugin.cpp | 19 ++--- .../src/render/shadow_atlas/shadow_atlas.cpp | 32 ++++---- .../render/shadow_atlas_rasterizer/plugin.cpp | 76 ++++++++++--------- 7 files changed, 125 insertions(+), 118 deletions(-) diff --git a/engine/assets/render/deferred_shading.fs b/engine/assets/render/deferred_shading.fs index 40a9fa4898..03ea366243 100644 --- a/engine/assets/render/deferred_shading.fs +++ b/engine/assets/render/deferred_shading.fs @@ -6,8 +6,8 @@ uniform sampler2D positionTexture; uniform sampler2D normalTexture; uniform sampler2D albedoTexture; uniform sampler2D ssaoTexture; -uniform sampler2D shadowAtlasTexture; -uniform sampler2DArray shadowCubeAtlasTexture; +uniform sampler2D spotShadowAtlasTexture; +uniform sampler2DArray pointShadowAtlasTexture; uniform sampler2DArray directionalShadowMap; // only one directional light with shadows is supported, for now @@ -118,7 +118,7 @@ vec3 spotLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI) // PCF if (spotLights[lightI].shadowBlurRadius <= 0.001f) { - float pcfDepth = texture(shadowAtlasTexture, uv).r; + float pcfDepth = texture(spotShadowAtlasTexture, uv).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } else @@ -130,7 +130,7 @@ vec3 spotLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI) { float x = spotLights[lightI].shadowBlurRadius*float(xi); float y = spotLights[lightI].shadowBlurRadius*float(yi); - float pcfDepth = texture(shadowAtlasTexture, uv + vec2(x, y) * texelSize).r; + float pcfDepth = texture(spotShadowAtlasTexture, uv + vec2(x, y) * texelSize).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } } @@ -261,7 +261,7 @@ vec3 pointLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI) // PCF if (pointLights[lightI].shadowBlurRadius <= 0.001f) { - float pcfDepth = texture(shadowCubeAtlasTexture, vec3(uv.xy, face)).r; + float pcfDepth = texture(pointShadowAtlasTexture, vec3(uv.xy, face)).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } else @@ -274,7 +274,7 @@ vec3 pointLightCalc(vec3 fragPos, vec3 fragNormal, uint lightI) float x = pointLights[lightI].shadowBlurRadius*float(xi); float y = pointLights[lightI].shadowBlurRadius*float(yi); vec2 newUv = uv + vec2(x, y) * texelSize; - float pcfDepth = texture(shadowCubeAtlasTexture, vec3(newUv.xy, face)).r; + float pcfDepth = texture(pointShadowAtlasTexture, vec3(newUv.xy, face)).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } } diff --git a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp index 1143671877..fa17012f43 100644 --- a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp +++ b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp @@ -15,47 +15,49 @@ namespace cubos::engine { - /// @brief Resource which stores the shadow map atlas, a large texture that - /// holds the shadow maps for each shadow caster in a quadtree structure, - /// reducing texture switching. + /// @brief Resource which stores shadow map atlases, large textures that + /// hold the shadow maps for each shadow caster in a quadtree structure, + /// reducing texture switching. There is an atlas for spot lights, and another + /// for point lights. /// @ingroup render-shadow-atlas-plugin class CUBOS_ENGINE_API ShadowAtlas { public: CUBOS_REFLECT; - /// @brief Gets the size of the shadow atlas texture. - /// @return Size of the shadow atlas texture, in pixels. - glm::uvec2 getSize() const; + /// @brief Gets the size of the spot shadow atlas texture. + /// @return Size of the spot shadow atlas texture, in pixels. + glm::uvec2 getSpotAtlasSize() const; - /// @brief Gets the size of the shadow cube atlas texture. - /// @return Size of the shadow cube atlas texture, in pixels. - glm::uvec2 getCubeSize() const; + /// @brief Gets the size of the point shadow atlas texture. + /// @return Size of the point shadow atlas texture, in pixels. + glm::uvec2 getPointAtlasSize() const; /// @brief Recreates the shadow atlas textures. /// @param rd Render device used to create the textures. void resize(cubos::core::gl::RenderDevice& rd); - /// @brief Configured size of the shadow atlas texture, in pixels. - /// Use this to change the resolution of the atlas. Note that the - /// texture isn't immediately resized; use @ref getSize() to get the + /// @brief Configured size of the spot shadow atlas texture, in pixels. + /// Use this to change the resolution of the spot atlas. Note that the + /// texture isn't immediately resized; use @ref getSpotAtlasSize() to get the /// actual texture size. - glm::uvec2 configSize = {4096, 4096}; + glm::uvec2 configSpotAtlasSize = {4096, 4096}; - /// @brief Configured size of the cube shadow atlas texture, in pixels. - /// Use this to change the resolution of the cube atlas. Note that the - /// texture isn't immediately resized; use @ref getCubeSize() to get the + /// @brief Configured size of the point shadow atlas texture, in pixels. + /// Use this to change the resolution of the point atlas. Note that the + /// texture isn't immediately resized; use @ref getPointAtlasSize() to get the /// actual texture size. - glm::uvec2 configCubeSize = {1024, 1024}; + glm::uvec2 configPointAtlasSize = {1024, 1024}; - /// @brief Whether the shadow atlas texture has already been cleared this frame. + /// @brief Whether the shadow atlas textures have already been cleared this frame. bool cleared = false; /// @brief Stores shadow maps for each spot shadow caster component. - core::gl::Texture2D atlas{nullptr}; + core::gl::Texture2D spotAtlas{nullptr}; /// @brief Stores shadow maps for each point shadow caster component. - core::gl::Texture2DArray cubeAtlas{nullptr}; + /// Each texture of the array corresponds to a face of a cubemap. + core::gl::Texture2DArray pointAtlas{nullptr}; /// @brief Slot for a shadow map in the shadow atlas. struct Slot @@ -75,21 +77,21 @@ namespace cubos::engine }; /// @brief Stores the sizes, offsets, and caster ids of the shadow maps - /// in the atlas. - std::vector> slots; + /// in the spot atlas. + std::vector> spotAtlasSlots; /// @brief Stores the sizes, offsets, and caster ids of the shadow maps - /// in the cube atlas. - std::vector> cubeSlots; + /// in the point atlas. + std::vector> pointAtlasSlots; /// @brief Maps shadow caster ids to their corresponding slots. std::map> slotsMap; private: - /// @brief Size of the shadow atlas texture, in pixels. - glm::uvec2 mSize = {0, 0}; + /// @brief Size of the spot shadow atlas texture, in pixels. + glm::uvec2 mSpotAtlasSize = {0, 0}; - /// @brief Size of the cube shadow atlas texture, in pixels. - glm::uvec2 mCubeSize = {0, 0}; + /// @brief Size of the point shadow atlas texture, in pixels. + glm::uvec2 mPointAtlasSize = {0, 0}; }; } // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp b/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp index 0e397867dc..84ee44c253 100644 --- a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp +++ b/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp @@ -17,13 +17,13 @@ namespace cubos::engine { CUBOS_REFLECT; - /// @brief Framebuffer used by the rasterizer to render to the ShadowAtlas. - core::gl::Framebuffer framebuffer{nullptr}; + /// @brief Framebuffer used by the rasterizer to render to the spot shadow atlas. + core::gl::Framebuffer spotAtlasFramebuffer{nullptr}; - /// @brief Framebuffer used by the rasterizer to render to the cube ShadowAtlas. - core::gl::Framebuffer cubeFramebuffer{nullptr}; + /// @brief Framebuffer used by the rasterizer to render to the point shadow atlas. + core::gl::Framebuffer pointAtlasFramebuffer{nullptr}; - /// @brief Atlas texture in the current framebuffer. + /// @brief Atlas texture stored to check if the framebuffers need to be recreated. core::gl::Texture2D atlas{nullptr}; }; } // namespace cubos::engine diff --git a/engine/src/render/deferred_shading/plugin.cpp b/engine/src/render/deferred_shading/plugin.cpp index 45b54950a8..389c834011 100644 --- a/engine/src/render/deferred_shading/plugin.cpp +++ b/engine/src/render/deferred_shading/plugin.cpp @@ -126,8 +126,8 @@ namespace ShaderBindingPoint normalBP; ShaderBindingPoint albedoBP; ShaderBindingPoint ssaoBP; - ShaderBindingPoint shadowAtlasBP; - ShaderBindingPoint shadowCubeAtlasBP; + ShaderBindingPoint spotShadowAtlasBP; + ShaderBindingPoint pointShadowAtlasBP; ShaderBindingPoint directionalShadowMapBP; ShaderBindingPoint perSceneBP; ShaderBindingPoint viewportOffsetBP; @@ -145,16 +145,16 @@ namespace normalBP = pipeline->getBindingPoint("normalTexture"); albedoBP = pipeline->getBindingPoint("albedoTexture"); ssaoBP = pipeline->getBindingPoint("ssaoTexture"); - shadowAtlasBP = pipeline->getBindingPoint("shadowAtlasTexture"); - shadowCubeAtlasBP = pipeline->getBindingPoint("shadowCubeAtlasTexture"); + spotShadowAtlasBP = pipeline->getBindingPoint("spotShadowAtlasTexture"); + pointShadowAtlasBP = pipeline->getBindingPoint("pointShadowAtlasTexture"); directionalShadowMapBP = pipeline->getBindingPoint("directionalShadowMap"); perSceneBP = pipeline->getBindingPoint("PerScene"); viewportOffsetBP = pipeline->getBindingPoint("viewportOffset"); viewportSizeBP = pipeline->getBindingPoint("viewportSize"); - CUBOS_ASSERT(positionBP && normalBP && albedoBP && ssaoBP && shadowAtlasBP && shadowCubeAtlasBP && + CUBOS_ASSERT(positionBP && normalBP && albedoBP && ssaoBP && spotShadowAtlasBP && pointShadowAtlasBP && directionalShadowMapBP && perSceneBP && viewportOffsetBP && viewportSizeBP, - "positionTexture, normalTexture, albedoTexture, ssaoTexture, shadowAtlasTexture" - "shadowCubeAtlasTexture, directionalShadowMap, PerScene, " + "positionTexture, normalTexture, albedoTexture, ssaoTexture, spotShadowAtlasTexture" + "pointShadowAtlasTexture, directionalShadowMap, PerScene, " "viewportOffset and " "viewportSize binding points must exist"); @@ -362,10 +362,11 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) // Get light viewport auto slot = shadowAtlas.slotsMap.at(caster.value().baseSettings.id); - auto lightProj = glm::perspective(glm::radians(90.0F), - (float(shadowAtlas.getCubeSize().x) * slot->size.x) / - (float(shadowAtlas.getCubeSize().y) * slot->size.y), - 0.1F, light.range); + auto lightProj = + glm::perspective(glm::radians(90.0F), + (float(shadowAtlas.getPointAtlasSize().x) * slot->size.x) / + (float(shadowAtlas.getPointAtlasSize().y) * slot->size.y), + 0.1F, light.range); for (int i = 0; i < 6; i++) { @@ -446,10 +447,11 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) auto lightView = glm::inverse(glm::scale( glm::rotate(lightLocalToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - auto lightProj = glm::perspective(glm::radians(light.spotAngle), - (float(shadowAtlas.getSize().x) * slot->size.x) / - (float(shadowAtlas.getSize().y) * slot->size.y), - 0.1F, light.range); + auto lightProj = + glm::perspective(glm::radians(light.spotAngle), + (float(shadowAtlas.getSpotAtlasSize().x) * slot->size.x) / + (float(shadowAtlas.getSpotAtlasSize().y) * slot->size.y), + 0.1F, light.range); perLight.matrix = lightProj * lightView; perLight.shadowMapOffset = slot->offset; perLight.shadowMapSize = slot->size; @@ -483,8 +485,8 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) state.normalBP->bind(gBuffer.normal); state.albedoBP->bind(gBuffer.albedo); state.ssaoBP->bind(ssao.blurTexture); - state.shadowAtlasBP->bind(shadowAtlas.atlas); - state.shadowCubeAtlasBP->bind(shadowAtlas.cubeAtlas); + state.spotShadowAtlasBP->bind(shadowAtlas.spotAtlas); + state.pointShadowAtlasBP->bind(shadowAtlas.pointAtlas); // directionalShadowMap needs to be bound even if it's null, or else errors may occur on some GPUs state.directionalShadowMapBP->bind(directionalShadowMap); state.directionalShadowMapBP->bind(state.directionalShadowSampler); diff --git a/engine/src/render/shadow_atlas/plugin.cpp b/engine/src/render/shadow_atlas/plugin.cpp index c10509dbeb..7806184c48 100644 --- a/engine/src/render/shadow_atlas/plugin.cpp +++ b/engine/src/render/shadow_atlas/plugin.cpp @@ -80,12 +80,13 @@ void cubos::engine::shadowAtlasPlugin(Cubos& cubos) cubos.tag(drawToShadowAtlasTag).after(reserveShadowCastersTag); cubos.system("create ShadowAtlas").tagged(createShadowAtlasTag).call([](const Window& window, ShadowAtlas& atlas) { - if (atlas.getSize() != atlas.configSize || atlas.getCubeSize() != atlas.configCubeSize) + if (atlas.getSpotAtlasSize() != atlas.configSpotAtlasSize || + atlas.getPointAtlasSize() != atlas.configPointAtlasSize) { atlas.resize(window->renderDevice()); - CUBOS_INFO("Resized ShadowAtlas to {}x{} (spot), {}x{} (point)", atlas.getSize().x, atlas.getSize().y, - atlas.getCubeSize().x, atlas.getCubeSize().y); + CUBOS_INFO("Resized ShadowAtlas to {}x{} (spot), {}x{} (point)", atlas.getSpotAtlasSize().x, + atlas.getSpotAtlasSize().y, atlas.getPointAtlasSize().x, atlas.getPointAtlasSize().y); } // New frame, hint that the shadow atlas texture needs to be cleared. @@ -95,24 +96,24 @@ void cubos::engine::shadowAtlasPlugin(Cubos& cubos) cubos.system("reserve space for shadow casters") .tagged(reserveShadowCastersTag) .call([](ShadowAtlas& atlas, Query spotCasters, Query pointCasters) { - atlas.slots.clear(); - atlas.cubeSlots.clear(); + atlas.spotAtlasSlots.clear(); + atlas.pointAtlasSlots.clear(); atlas.slotsMap.clear(); - atlas.slots.push_back( + atlas.spotAtlasSlots.push_back( std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); - atlas.cubeSlots.push_back( + atlas.pointAtlasSlots.push_back( std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); int id = 1; for (auto [caster] : spotCasters) { - reserveCasterSlot(atlas.slots, atlas.slotsMap, caster.baseSettings, id); + reserveCasterSlot(atlas.spotAtlasSlots, atlas.slotsMap, caster.baseSettings, id); } for (auto [caster] : pointCasters) { - reserveCasterSlot(atlas.cubeSlots, atlas.slotsMap, caster.baseSettings, id); + reserveCasterSlot(atlas.pointAtlasSlots, atlas.slotsMap, caster.baseSettings, id); } }); } diff --git a/engine/src/render/shadow_atlas/shadow_atlas.cpp b/engine/src/render/shadow_atlas/shadow_atlas.cpp index 716553a8c8..fb28a46883 100644 --- a/engine/src/render/shadow_atlas/shadow_atlas.cpp +++ b/engine/src/render/shadow_atlas/shadow_atlas.cpp @@ -12,45 +12,45 @@ using cubos::core::gl::Usage; CUBOS_REFLECT_IMPL(cubos::engine::ShadowAtlas) { return core::ecs::TypeBuilder("cubos::engine::ShadowAtlas") - .withField("configSize", &ShadowAtlas::configSize) - .withField("configCubeSize", &ShadowAtlas::configCubeSize) + .withField("configSpotAtlasSize", &ShadowAtlas::configSpotAtlasSize) + .withField("configPointAtlasSize", &ShadowAtlas::configPointAtlasSize) .withField("cleared", &ShadowAtlas::cleared) .build(); } -glm::uvec2 cubos::engine::ShadowAtlas::getSize() const +glm::uvec2 cubos::engine::ShadowAtlas::getSpotAtlasSize() const { - return mSize; + return mSpotAtlasSize; } -glm::uvec2 cubos::engine::ShadowAtlas::getCubeSize() const +glm::uvec2 cubos::engine::ShadowAtlas::getPointAtlasSize() const { - return mCubeSize; + return mPointAtlasSize; } void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) { - mSize = configSize; - mCubeSize = configCubeSize; + mSpotAtlasSize = configSpotAtlasSize; + mPointAtlasSize = configPointAtlasSize; // Prepare texture description. Texture2DDesc desc{}; - desc.width = mSize.x; - desc.height = mSize.y; + desc.width = mSpotAtlasSize.x; + desc.height = mSpotAtlasSize.y; desc.usage = Usage::Dynamic; - // Create shadow atlas texture. + // Create spot shadow atlas texture. desc.format = TextureFormat::Depth32; - atlas = rd.createTexture2D(desc); + spotAtlas = rd.createTexture2D(desc); // Prepare texture array description. Texture2DArrayDesc cubeDesc{}; - cubeDesc.width = mCubeSize.x; - cubeDesc.height = mCubeSize.y; + cubeDesc.width = mPointAtlasSize.x; + cubeDesc.height = mPointAtlasSize.y; cubeDesc.size = 6; cubeDesc.usage = Usage::Dynamic; - // Create shadow cube atlas texture. + // Create point shadow atlas texture. cubeDesc.format = TextureFormat::Depth32; - cubeAtlas = rd.createTexture2DArray(cubeDesc); + pointAtlas = rd.createTexture2DArray(cubeDesc); } diff --git a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp index cbfcdd6312..568a3cfa36 100644 --- a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp +++ b/engine/src/render/shadow_atlas_rasterizer/plugin.cpp @@ -151,36 +151,37 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) Query meshes) { auto& rd = window->renderDevice(); - // Check if we need to recreate the framebuffer. - if (rasterizer.atlas != atlas.atlas) + // Check if we need to recreate the framebuffers. + if (rasterizer.atlas != atlas.spotAtlas) { // Store textures so we can check if they change in the next frame. - rasterizer.atlas = atlas.atlas; + rasterizer.atlas = atlas.spotAtlas; - // Create the framebuffer. + // Create the framebuffers. FramebufferDesc desc{}; desc.targetCount = 0; - desc.depthStencil.setTexture2DTarget(atlas.atlas); - rasterizer.framebuffer = rd.createFramebuffer(desc); + desc.depthStencil.setTexture2DTarget(atlas.spotAtlas); + rasterizer.spotAtlasFramebuffer = rd.createFramebuffer(desc); FramebufferDesc cubeDesc{}; cubeDesc.targetCount = 0; - cubeDesc.depthStencil.setTexture2DArrayTarget(atlas.cubeAtlas); - rasterizer.cubeFramebuffer = rd.createFramebuffer(cubeDesc); + cubeDesc.depthStencil.setTexture2DArrayTarget(atlas.pointAtlas); + rasterizer.pointAtlasFramebuffer = rd.createFramebuffer(cubeDesc); - CUBOS_INFO("Recreated ShadowAtlasRasterizer's framebuffer"); + CUBOS_INFO("Recreated ShadowAtlasRasterizer's framebuffers"); } - // Bind the framebuffer and set the viewport. - rd.setFramebuffer(rasterizer.framebuffer); - rd.setViewport(0, 0, static_cast(atlas.getSize().x), static_cast(atlas.getSize().y)); + // Bind the spot atlas framebuffer and set the viewport. + rd.setFramebuffer(rasterizer.spotAtlasFramebuffer); + rd.setViewport(0, 0, static_cast(atlas.getSpotAtlasSize().x), + static_cast(atlas.getSpotAtlasSize().y)); // Set the raster and depth-stencil states. rd.setRasterState(state.rasterState); rd.setBlendState(nullptr); rd.setDepthStencilState(state.depthStencilState); - // Clear + // Clear spot atlas if (!atlas.cleared) { rd.clearDepth(1.0F); @@ -197,21 +198,21 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) glm::scale(glm::rotate(localToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), glm::vec3(1.0F / localToWorld.worldScale()))); auto proj = glm::perspective(glm::radians(light.spotAngle), - (float(atlas.getSize().x) * slot->size.x) / - (float(atlas.getSize().y) * slot->size.y), + (float(atlas.getSpotAtlasSize().x) * slot->size.x) / + (float(atlas.getSpotAtlasSize().y) * slot->size.y), 0.1F, light.range); PerScene perScene{.lightViewProj = proj * view}; state.perSceneCB->fill(&perScene, sizeof(perScene)); // Set the viewport. - rd.setViewport(static_cast(slot->offset.x * float(atlas.getSize().x)), - static_cast(slot->offset.y * float(atlas.getSize().y)), - static_cast(slot->size.x * float(atlas.getSize().x)), - static_cast(slot->size.y * float(atlas.getSize().y))); - rd.setScissor(static_cast(slot->offset.x * float(atlas.getSize().x)), - static_cast(slot->offset.y * float(atlas.getSize().y)), - static_cast(slot->size.x * float(atlas.getSize().x)), - static_cast(slot->size.y * float(atlas.getSize().y))); + rd.setViewport(static_cast(slot->offset.x * float(atlas.getSpotAtlasSize().x)), + static_cast(slot->offset.y * float(atlas.getSpotAtlasSize().y)), + static_cast(slot->size.x * float(atlas.getSpotAtlasSize().x)), + static_cast(slot->size.y * float(atlas.getSpotAtlasSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(atlas.getSpotAtlasSize().x)), + static_cast(slot->offset.y * float(atlas.getSpotAtlasSize().y)), + static_cast(slot->size.x * float(atlas.getSpotAtlasSize().x)), + static_cast(slot->size.y * float(atlas.getSpotAtlasSize().y))); // Bind the shader, vertex array and uniform buffer. rd.setShaderPipeline(state.pipeline); rd.setVertexArray(state.vertexArray); @@ -236,11 +237,12 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) } } - // Bind the framebuffer and set the viewport. - rd.setFramebuffer(rasterizer.cubeFramebuffer); - rd.setViewport(0, 0, static_cast(atlas.getCubeSize().x), static_cast(atlas.getCubeSize().y)); + // Bind the point atlas framebuffer and set the viewport. + rd.setFramebuffer(rasterizer.pointAtlasFramebuffer); + rd.setViewport(0, 0, static_cast(atlas.getPointAtlasSize().x), + static_cast(atlas.getPointAtlasSize().y)); - // Clear + // Clear point atlas if (!atlas.cleared) { rd.clearDepth(1.0F); @@ -253,20 +255,20 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) auto slot = atlas.slotsMap.at(caster.baseSettings.id); // Set the viewport. - rd.setViewport(static_cast(slot->offset.x * float(atlas.getCubeSize().x)), - static_cast(slot->offset.y * float(atlas.getCubeSize().y)), - static_cast(slot->size.x * float(atlas.getCubeSize().x)), - static_cast(slot->size.y * float(atlas.getCubeSize().y))); - rd.setScissor(static_cast(slot->offset.x * float(atlas.getCubeSize().x)), - static_cast(slot->offset.y * float(atlas.getCubeSize().y)), - static_cast(slot->size.x * float(atlas.getCubeSize().x)), - static_cast(slot->size.y * float(atlas.getCubeSize().y))); + rd.setViewport(static_cast(slot->offset.x * float(atlas.getPointAtlasSize().x)), + static_cast(slot->offset.y * float(atlas.getPointAtlasSize().y)), + static_cast(slot->size.x * float(atlas.getPointAtlasSize().x)), + static_cast(slot->size.y * float(atlas.getPointAtlasSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(atlas.getPointAtlasSize().x)), + static_cast(slot->offset.y * float(atlas.getPointAtlasSize().y)), + static_cast(slot->size.x * float(atlas.getPointAtlasSize().x)), + static_cast(slot->size.y * float(atlas.getPointAtlasSize().y))); // Send the PerScene data to the GPU. PerSceneCube perScene; auto proj = glm::perspective(glm::radians(90.0F), - (float(atlas.getCubeSize().x) * slot->size.x) / - (float(atlas.getCubeSize().y) * slot->size.y), + (float(atlas.getPointAtlasSize().x) * slot->size.x) / + (float(atlas.getPointAtlasSize().y) * slot->size.y), 0.1F, light.range); for (int i = 0; i < 6; i++) From 727a34966736d6eea8e7508e28e89ccc01da602b Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:52:20 +0000 Subject: [PATCH 8/9] refactor(rendering): move shadows code to a single folder --- core/include/cubos/core/geom/utils.hpp | 5 + core/src/geom/utils.cpp | 16 ++ engine/CMakeLists.txt | 23 +-- .../render/shadow_atlas/shadow_atlas.hpp | 97 ------------ .../atlas}/plugin.hpp | 10 +- .../render/shadows/atlas/point_atlas.hpp | 60 +++++++ .../engine/render/shadows/atlas/slot.hpp | 29 ++++ .../render/shadows/atlas/spot_atlas.hpp | 59 +++++++ .../atlas_rasterizer/atlas_rasterizer.hpp} | 7 +- .../atlas_rasterizer}/plugin.hpp | 4 +- .../cascaded}/plugin.hpp | 4 +- .../cascaded_rasterizer}/plugin.hpp | 4 +- .../render/shadows/{ => casters}/caster.hpp | 4 +- .../{ => casters}/directional_caster.hpp | 6 +- .../render/shadows/{ => casters}/plugin.hpp | 12 +- .../shadows/{ => casters}/point_caster.hpp | 6 +- .../shadows/{ => casters}/spot_caster.hpp | 6 +- .../cubos/engine/render/shadows/module.dox | 9 ++ engine/src/render/defaults/plugin.cpp | 12 +- engine/src/render/deferred_shading/plugin.cpp | 97 ++++-------- engine/src/render/shadow_atlas/plugin.cpp | 119 -------------- .../src/render/shadow_atlas/shadow_atlas.cpp | 56 ------- engine/src/render/shadows/atlas/plugin.cpp | 133 ++++++++++++++++ .../src/render/shadows/atlas/point_atlas.cpp | 38 +++++ .../src/render/shadows/atlas/spot_atlas.cpp | 37 +++++ .../atlas_rasterizer/atlas_rasterizer.cpp} | 2 +- .../atlas_rasterizer}/plugin.cpp | 148 ++++++++---------- .../cascaded}/plugin.cpp | 8 +- .../cascaded_rasterizer}/plugin.cpp | 10 +- .../render/shadows/{ => casters}/caster.cpp | 2 +- .../{ => casters}/directional_caster.cpp | 2 +- engine/src/render/shadows/casters/plugin.cpp | 12 ++ .../shadows/{ => casters}/point_caster.cpp | 2 +- .../shadows/{ => casters}/spot_caster.cpp | 2 +- engine/src/render/shadows/plugin.cpp | 12 -- 35 files changed, 555 insertions(+), 498 deletions(-) delete mode 100644 engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp rename engine/include/cubos/engine/render/{shadow_atlas => shadows/atlas}/plugin.hpp (77%) create mode 100644 engine/include/cubos/engine/render/shadows/atlas/point_atlas.hpp create mode 100644 engine/include/cubos/engine/render/shadows/atlas/slot.hpp create mode 100644 engine/include/cubos/engine/render/shadows/atlas/spot_atlas.hpp rename engine/include/cubos/engine/render/{shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp => shadows/atlas_rasterizer/atlas_rasterizer.hpp} (73%) rename engine/include/cubos/engine/render/{shadow_atlas_rasterizer => shadows/atlas_rasterizer}/plugin.hpp (91%) rename engine/include/cubos/engine/render/{cascaded_shadow_maps => shadows/cascaded}/plugin.hpp (92%) rename engine/include/cubos/engine/render/{cascaded_shadow_maps_rasterizer => shadows/cascaded_rasterizer}/plugin.hpp (92%) rename engine/include/cubos/engine/render/shadows/{ => casters}/caster.hpp (88%) rename engine/include/cubos/engine/render/shadows/{ => casters}/directional_caster.hpp (95%) rename engine/include/cubos/engine/render/shadows/{ => casters}/plugin.hpp (50%) rename engine/include/cubos/engine/render/shadows/{ => casters}/point_caster.hpp (74%) rename engine/include/cubos/engine/render/shadows/{ => casters}/spot_caster.hpp (74%) create mode 100644 engine/include/cubos/engine/render/shadows/module.dox delete mode 100644 engine/src/render/shadow_atlas/plugin.cpp delete mode 100644 engine/src/render/shadow_atlas/shadow_atlas.cpp create mode 100644 engine/src/render/shadows/atlas/plugin.cpp create mode 100644 engine/src/render/shadows/atlas/point_atlas.cpp create mode 100644 engine/src/render/shadows/atlas/spot_atlas.cpp rename engine/src/render/{shadow_atlas_rasterizer/shadow_atlas_rasterizer.cpp => shadows/atlas_rasterizer/atlas_rasterizer.cpp} (71%) rename engine/src/render/{shadow_atlas_rasterizer => shadows/atlas_rasterizer}/plugin.cpp (71%) rename engine/src/render/{cascaded_shadow_maps => shadows/cascaded}/plugin.cpp (91%) rename engine/src/render/{cascaded_shadow_maps_rasterizer => shadows/cascaded_rasterizer}/plugin.cpp (97%) rename engine/src/render/shadows/{ => casters}/caster.cpp (89%) rename engine/src/render/shadows/{ => casters}/directional_caster.cpp (97%) create mode 100644 engine/src/render/shadows/casters/plugin.cpp rename engine/src/render/shadows/{ => casters}/point_caster.cpp (85%) rename engine/src/render/shadows/{ => casters}/spot_caster.cpp (85%) delete mode 100644 engine/src/render/shadows/plugin.cpp diff --git a/core/include/cubos/core/geom/utils.hpp b/core/include/cubos/core/geom/utils.hpp index 3331216f1c..a8b8da8049 100644 --- a/core/include/cubos/core/geom/utils.hpp +++ b/core/include/cubos/core/geom/utils.hpp @@ -62,4 +62,9 @@ namespace cubos::core::geom /// @param corners Output vector where the corners will be stored. CUBOS_CORE_API void getCameraFrustumCorners(const glm::mat4& view, const glm::mat4& proj, float zNear, float zFar, std::vector& corners); + + /// @brief Gets view matrices for rendering a cubemap. + /// @param inverseView Matrix that transforms the camera's view space to world space. + /// @param cubeViewMatrices Output vector where the view matrices will be stored. + CUBOS_CORE_API void getCubeViewMatrices(const glm::mat4& inverseView, std::vector& cubeViewMatrices); } // namespace cubos::core::geom diff --git a/core/src/geom/utils.cpp b/core/src/geom/utils.cpp index cc1ef419cd..1ee12d3b80 100644 --- a/core/src/geom/utils.cpp +++ b/core/src/geom/utils.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -191,3 +192,18 @@ void cubos::core::geom::getCameraFrustumCorners(const glm::mat4& view, const glm } } } + +void cubos::core::geom::getCubeViewMatrices(const glm::mat4& inverseView, std::vector& cubeViewMatrices) +{ + static const float ViewAngle[6] = {0.0F, 180.0F, 90.0F, -90.0F, 90.0F, -90.0F}; + static const glm::vec3 ViewAxis[6] = {glm::vec3(0.0F, 1.0F, 0.0F), glm::vec3(0.0F, 1.0F, 0.0F), + glm::vec3(0.0F, 1.0F, 0.0F), glm::vec3(0.0F, 1.0F, 0.0F), + glm::vec3(1.0F, 0.0F, 0.0F), glm::vec3(1.0F, 0.0, 0.0F)}; + + cubeViewMatrices.resize(6); + + for (size_t i = 0; i < 6; i++) + { + cubeViewMatrices[i] = glm::inverse(glm::rotate(inverseView, glm::radians(ViewAngle[i]), ViewAxis[i])); + } +} diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 8745ab695c..7b55ef8e9e 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -177,17 +177,18 @@ set(CUBOS_ENGINE_SOURCE "src/render/split_screen/split_screen.cpp" "src/render/bloom/plugin.cpp" "src/render/bloom/bloom.cpp" - "src/render/shadows/plugin.cpp" - "src/render/shadows/caster.cpp" - "src/render/shadows/spot_caster.cpp" - "src/render/shadows/directional_caster.cpp" - "src/render/shadows/point_caster.cpp" - "src/render/shadow_atlas/plugin.cpp" - "src/render/shadow_atlas/shadow_atlas.cpp" - "src/render/shadow_atlas_rasterizer/plugin.cpp" - "src/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.cpp" - "src/render/cascaded_shadow_maps/plugin.cpp" - "src/render/cascaded_shadow_maps_rasterizer/plugin.cpp" + "src/render/shadows/casters/plugin.cpp" + "src/render/shadows/casters/caster.cpp" + "src/render/shadows/casters/spot_caster.cpp" + "src/render/shadows/casters/directional_caster.cpp" + "src/render/shadows/casters/point_caster.cpp" + "src/render/shadows/atlas/plugin.cpp" + "src/render/shadows/atlas/spot_atlas.cpp" + "src/render/shadows/atlas/point_atlas.cpp" + "src/render/shadows/atlas_rasterizer/plugin.cpp" + "src/render/shadows/atlas_rasterizer/atlas_rasterizer.cpp" + "src/render/shadows/cascaded/plugin.cpp" + "src/render/shadows/cascaded_rasterizer/plugin.cpp" "src/tools/settings_inspector/plugin.cpp" "src/tools/selection/plugin.cpp" diff --git a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp b/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp deleted file mode 100644 index fa17012f43..0000000000 --- a/engine/include/cubos/engine/render/shadow_atlas/shadow_atlas.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/// @file -/// @brief Resource @ref cubos::engine::ShadowAtlas. -/// @ingroup render-shadow-atlas-plugin - -#pragma once - -#include - -#include - -#include -#include - -#include - -namespace cubos::engine -{ - /// @brief Resource which stores shadow map atlases, large textures that - /// hold the shadow maps for each shadow caster in a quadtree structure, - /// reducing texture switching. There is an atlas for spot lights, and another - /// for point lights. - /// @ingroup render-shadow-atlas-plugin - class CUBOS_ENGINE_API ShadowAtlas - { - public: - CUBOS_REFLECT; - - /// @brief Gets the size of the spot shadow atlas texture. - /// @return Size of the spot shadow atlas texture, in pixels. - glm::uvec2 getSpotAtlasSize() const; - - /// @brief Gets the size of the point shadow atlas texture. - /// @return Size of the point shadow atlas texture, in pixels. - glm::uvec2 getPointAtlasSize() const; - - /// @brief Recreates the shadow atlas textures. - /// @param rd Render device used to create the textures. - void resize(cubos::core::gl::RenderDevice& rd); - - /// @brief Configured size of the spot shadow atlas texture, in pixels. - /// Use this to change the resolution of the spot atlas. Note that the - /// texture isn't immediately resized; use @ref getSpotAtlasSize() to get the - /// actual texture size. - glm::uvec2 configSpotAtlasSize = {4096, 4096}; - - /// @brief Configured size of the point shadow atlas texture, in pixels. - /// Use this to change the resolution of the point atlas. Note that the - /// texture isn't immediately resized; use @ref getPointAtlasSize() to get the - /// actual texture size. - glm::uvec2 configPointAtlasSize = {1024, 1024}; - - /// @brief Whether the shadow atlas textures have already been cleared this frame. - bool cleared = false; - - /// @brief Stores shadow maps for each spot shadow caster component. - core::gl::Texture2D spotAtlas{nullptr}; - - /// @brief Stores shadow maps for each point shadow caster component. - /// Each texture of the array corresponds to a face of a cubemap. - core::gl::Texture2DArray pointAtlas{nullptr}; - - /// @brief Slot for a shadow map in the shadow atlas. - struct Slot - { - glm::vec2 size; ///< Shadow map size, in normalized coordinates. - glm::vec2 offset; ///< Shadow map offset, in normalized coordinates. - int casterId; ///< Id of the shadow caster (-1 if none). - - /// @brief Constructs. - /// @param size Shadow map size, in normalized coordinates. - /// @param offset Shadow map offset, in normalized coordinates. - /// @param casterId Id of the shadow caster (-1 if none). - Slot(glm::vec2 size, glm::vec2 offset, int casterId) - : size(size) - , offset(offset) - , casterId(casterId){}; - }; - - /// @brief Stores the sizes, offsets, and caster ids of the shadow maps - /// in the spot atlas. - std::vector> spotAtlasSlots; - - /// @brief Stores the sizes, offsets, and caster ids of the shadow maps - /// in the point atlas. - std::vector> pointAtlasSlots; - - /// @brief Maps shadow caster ids to their corresponding slots. - std::map> slotsMap; - - private: - /// @brief Size of the spot shadow atlas texture, in pixels. - glm::uvec2 mSpotAtlasSize = {0, 0}; - - /// @brief Size of the point shadow atlas texture, in pixels. - glm::uvec2 mPointAtlasSize = {0, 0}; - }; -} // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadow_atlas/plugin.hpp b/engine/include/cubos/engine/render/shadows/atlas/plugin.hpp similarity index 77% rename from engine/include/cubos/engine/render/shadow_atlas/plugin.hpp rename to engine/include/cubos/engine/render/shadows/atlas/plugin.hpp index d51f82fca3..5885161e66 100644 --- a/engine/include/cubos/engine/render/shadow_atlas/plugin.hpp +++ b/engine/include/cubos/engine/render/shadows/atlas/plugin.hpp @@ -13,14 +13,14 @@ namespace cubos::engine { /// @defgroup render-shadow-atlas-plugin Shadow atlas - /// @ingroup render-plugins - /// @brief Creates and manages a shadow map atlas. + /// @ingroup render-shadows-plugins + /// @brief Creates and manages shadow map atlases. /// /// ## Dependencies - /// - @ref render-shadows-plugin + /// - @ref render-shadow-casters-plugin /// - @ref window-plugin - /// @brief Creates the shadow atlas. + /// @brief Creates the shadow atlases. /// @ingroup render-shadow-atlas-plugin CUBOS_ENGINE_API extern Tag createShadowAtlasTag; @@ -28,7 +28,7 @@ namespace cubos::engine /// @ingroup render-shadow-atlas-plugin CUBOS_ENGINE_API extern Tag reserveShadowCastersTag; - /// @brief Systems which draw to the shadow atlas texture should be tagged with this. + /// @brief Systems which draw to the shadow atlas textures should be tagged with this. /// @ingroup render-shadow-atlas-plugin CUBOS_ENGINE_API extern Tag drawToShadowAtlasTag; diff --git a/engine/include/cubos/engine/render/shadows/atlas/point_atlas.hpp b/engine/include/cubos/engine/render/shadows/atlas/point_atlas.hpp new file mode 100644 index 0000000000..98508a849d --- /dev/null +++ b/engine/include/cubos/engine/render/shadows/atlas/point_atlas.hpp @@ -0,0 +1,60 @@ +/// @file +/// @brief Resource @ref cubos::engine::PointShadowAtlas. +/// @ingroup render-shadow-atlas-plugin + +#pragma once + +#include + +#include + +#include +#include + +#include +#include + +namespace cubos::engine +{ + /// @brief Resource which stores the shadow map atlas for point lights, + /// a large texture that holds the shadow maps for each shadow caster + /// in a quadtree structure, reducing texture switching. + /// @ingroup render-shadow-atlas-plugin + class CUBOS_ENGINE_API PointShadowAtlas + { + public: + CUBOS_REFLECT; + + /// @brief Gets the size of the shadow atlas texture. + /// @return Size of the shadow atlas texture, in pixels. + glm::uvec2 getSize() const; + + /// @brief Recreates the shadow atlas texture. + /// @param rd Render device used to create the texture. + void resize(cubos::core::gl::RenderDevice& rd); + + /// @brief Configured size of the shadow atlas texture, in pixels. + /// Use this to change the resolution of the atlas. Note that the + /// texture isn't immediately resized; use @ref getSize() to get the + /// actual texture size. + glm::uvec2 configSize = {1024, 1024}; + + /// @brief Whether the shadow atlas texture has already been cleared this frame. + bool cleared = false; + + /// @brief Stores shadow maps for each point shadow caster component. + /// Each texture of the array corresponds to a face of a cubemap. + core::gl::Texture2DArray atlas{nullptr}; + + /// @brief Stores the sizes, offsets, and caster ids of the shadow maps + /// in the atlas. + std::vector> slots; + + /// @brief Maps shadow caster ids to their corresponding slots. + std::map> slotsMap; + + private: + /// @brief Size of the shadow atlas texture, in pixels. + glm::uvec2 mSize = {0, 0}; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadows/atlas/slot.hpp b/engine/include/cubos/engine/render/shadows/atlas/slot.hpp new file mode 100644 index 0000000000..68d10967f3 --- /dev/null +++ b/engine/include/cubos/engine/render/shadows/atlas/slot.hpp @@ -0,0 +1,29 @@ +/// @file +/// @brief Resource @ref cubos::engine::ShadowMapSlot. +/// @ingroup render-shadow-atlas-plugin + +#pragma once + +#include + +#include + +namespace cubos::engine +{ + /// @brief Slot for a shadow map in the shadow atlas. + struct ShadowMapSlot + { + glm::vec2 size; ///< Shadow map size, in normalized coordinates. + glm::vec2 offset; ///< Shadow map offset, in normalized coordinates. + int casterId; ///< Id of the shadow caster (-1 if none). + + /// @brief Constructs. + /// @param size Shadow map size, in normalized coordinates. + /// @param offset Shadow map offset, in normalized coordinates. + /// @param casterId Id of the shadow caster (-1 if none). + ShadowMapSlot(glm::vec2 size, glm::vec2 offset, int casterId) + : size(size) + , offset(offset) + , casterId(casterId) {}; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadows/atlas/spot_atlas.hpp b/engine/include/cubos/engine/render/shadows/atlas/spot_atlas.hpp new file mode 100644 index 0000000000..fe6522c67c --- /dev/null +++ b/engine/include/cubos/engine/render/shadows/atlas/spot_atlas.hpp @@ -0,0 +1,59 @@ +/// @file +/// @brief Resource @ref cubos::engine::SpotShadowAtlas. +/// @ingroup render-shadow-atlas-plugin + +#pragma once + +#include + +#include + +#include +#include + +#include +#include + +namespace cubos::engine +{ + /// @brief Resource which stores the shadow map atlas for spot lights, + /// a large texture that holds the shadow maps for each shadow caster + /// in a quadtree structure, reducing texture switching. + /// @ingroup render-shadow-atlas-plugin + class CUBOS_ENGINE_API SpotShadowAtlas + { + public: + CUBOS_REFLECT; + + /// @brief Gets the size of the shadow atlas texture. + /// @return Size of the shadow atlas texture, in pixels. + glm::uvec2 getSize() const; + + /// @brief Recreates the shadow atlas texture. + /// @param rd Render device used to create the texture. + void resize(cubos::core::gl::RenderDevice& rd); + + /// @brief Configured size of the shadow atlas texture, in pixels. + /// Use this to change the resolution of the atlas. Note that the + /// texture isn't immediately resized; use @ref getSize() to get the + /// actual texture size. + glm::uvec2 configSize = {4096, 4096}; + + /// @brief Whether the shadow atlas texture has already been cleared this frame. + bool cleared = false; + + /// @brief Stores shadow maps for each spot shadow caster component. + core::gl::Texture2D atlas{nullptr}; + + /// @brief Stores the sizes, offsets, and caster ids of the shadow maps + /// in the atlas. + std::vector> slots; + + /// @brief Maps shadow caster ids to their corresponding slots. + std::map> slotsMap; + + private: + /// @brief Size of the shadow atlas texture, in pixels. + glm::uvec2 mSize = {0, 0}; + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp b/engine/include/cubos/engine/render/shadows/atlas_rasterizer/atlas_rasterizer.hpp similarity index 73% rename from engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp rename to engine/include/cubos/engine/render/shadows/atlas_rasterizer/atlas_rasterizer.hpp index 84ee44c253..2207e0ff1c 100644 --- a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.hpp +++ b/engine/include/cubos/engine/render/shadows/atlas_rasterizer/atlas_rasterizer.hpp @@ -23,7 +23,10 @@ namespace cubos::engine /// @brief Framebuffer used by the rasterizer to render to the point shadow atlas. core::gl::Framebuffer pointAtlasFramebuffer{nullptr}; - /// @brief Atlas texture stored to check if the framebuffers need to be recreated. - core::gl::Texture2D atlas{nullptr}; + /// @brief Atlas texture stored to check if the spot atlas framebuffer needs to be recreated. + core::gl::Texture2D spotAtlas{nullptr}; + + /// @brief Atlas texture stored to check if the point atlas framebuffer needs to be recreated. + core::gl::Texture2DArray pointAtlas{nullptr}; }; } // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/plugin.hpp b/engine/include/cubos/engine/render/shadows/atlas_rasterizer/plugin.hpp similarity index 91% rename from engine/include/cubos/engine/render/shadow_atlas_rasterizer/plugin.hpp rename to engine/include/cubos/engine/render/shadows/atlas_rasterizer/plugin.hpp index b3337fe3a4..3fcf0bf1c3 100644 --- a/engine/include/cubos/engine/render/shadow_atlas_rasterizer/plugin.hpp +++ b/engine/include/cubos/engine/render/shadows/atlas_rasterizer/plugin.hpp @@ -13,7 +13,7 @@ namespace cubos::engine { /// @defgroup render-shadow-atlas-rasterizer-plugin Shadow atlas rasterizer - /// @ingroup render-plugins + /// @ingroup render-shadows-plugins /// @brief Draws all render meshes for each light to the shadow map atlas. /// /// ## Dependencies @@ -22,7 +22,7 @@ namespace cubos::engine /// - @ref transform-plugin /// - @ref lights-plugin /// - @ref render-shadow-atlas-plugin - /// - @ref render-shadows-plugin + /// - @ref render-shadow-casters-plugin /// - @ref render-mesh-plugin /// - @ref render-voxels-plugin diff --git a/engine/include/cubos/engine/render/cascaded_shadow_maps/plugin.hpp b/engine/include/cubos/engine/render/shadows/cascaded/plugin.hpp similarity index 92% rename from engine/include/cubos/engine/render/cascaded_shadow_maps/plugin.hpp rename to engine/include/cubos/engine/render/shadows/cascaded/plugin.hpp index 10ce089a7e..b9f6282bfe 100644 --- a/engine/include/cubos/engine/render/cascaded_shadow_maps/plugin.hpp +++ b/engine/include/cubos/engine/render/shadows/cascaded/plugin.hpp @@ -13,12 +13,12 @@ namespace cubos::engine { /// @defgroup render-cascaded-shadow-maps-plugin Cascaded shadow maps - /// @ingroup render-plugins + /// @ingroup render-shadows-plugins /// @brief Creates and manages shadow maps for directional shadow casters. /// /// ## Dependencies /// - @ref render-camera-plugin - /// - @ref render-shadows-plugin + /// - @ref render-shadow-casters-plugin /// - @ref window-plugin /// @brief Creates the shadow maps. diff --git a/engine/include/cubos/engine/render/cascaded_shadow_maps_rasterizer/plugin.hpp b/engine/include/cubos/engine/render/shadows/cascaded_rasterizer/plugin.hpp similarity index 92% rename from engine/include/cubos/engine/render/cascaded_shadow_maps_rasterizer/plugin.hpp rename to engine/include/cubos/engine/render/shadows/cascaded_rasterizer/plugin.hpp index 22a35dff72..88abfe1454 100644 --- a/engine/include/cubos/engine/render/cascaded_shadow_maps_rasterizer/plugin.hpp +++ b/engine/include/cubos/engine/render/shadows/cascaded_rasterizer/plugin.hpp @@ -13,7 +13,7 @@ namespace cubos::engine { /// @defgroup render-cascaded-shadow-maps-rasterizer-plugin Cascaded shadow maps rasterizer - /// @ingroup render-plugins + /// @ingroup render-shadows-plugins /// @brief Draws all render meshes for each directional light to its shadow map. /// /// ## Dependencies @@ -22,7 +22,7 @@ namespace cubos::engine /// - @ref transform-plugin /// - @ref lights-plugin /// - @ref render-cascaded-shadow-maps-plugin - /// - @ref render-shadows-plugin + /// - @ref render-shadow-casters-plugin /// - @ref render-mesh-plugin /// - @ref render-voxels-plugin /// - @ref render-camera-plugin diff --git a/engine/include/cubos/engine/render/shadows/caster.hpp b/engine/include/cubos/engine/render/shadows/casters/caster.hpp similarity index 88% rename from engine/include/cubos/engine/render/shadows/caster.hpp rename to engine/include/cubos/engine/render/shadows/casters/caster.hpp index bb0d96b87d..fa3faa31bb 100644 --- a/engine/include/cubos/engine/render/shadows/caster.hpp +++ b/engine/include/cubos/engine/render/shadows/casters/caster.hpp @@ -1,6 +1,6 @@ /// @file /// @brief Struct @ref cubos::engine::ShadowCaster. -/// @ingroup render-shadows-plugin +/// @ingroup render-shadow-casters-plugin #pragma once @@ -13,7 +13,7 @@ namespace cubos::engine { /// @brief Struct which contains the settings common to all shadow casters. - /// @ingroup render-shadows-plugin + /// @ingroup render-shadow-casters-plugin struct CUBOS_ENGINE_API ShadowCaster { CUBOS_REFLECT; diff --git a/engine/include/cubos/engine/render/shadows/directional_caster.hpp b/engine/include/cubos/engine/render/shadows/casters/directional_caster.hpp similarity index 95% rename from engine/include/cubos/engine/render/shadows/directional_caster.hpp rename to engine/include/cubos/engine/render/shadows/casters/directional_caster.hpp index 0413b39eb9..c28b001c8f 100644 --- a/engine/include/cubos/engine/render/shadows/directional_caster.hpp +++ b/engine/include/cubos/engine/render/shadows/casters/directional_caster.hpp @@ -1,6 +1,6 @@ /// @file /// @brief Component @ref cubos::engine::DirectionalShadowCaster. -/// @ingroup render-shadows-plugin +/// @ingroup render-shadow-casters-plugin #pragma once @@ -13,12 +13,12 @@ #include #include -#include +#include namespace cubos::engine { /// @brief Component which enables shadow casting on a directional light. - /// @ingroup render-shadows-plugin + /// @ingroup render-shadow-casters-plugin struct CUBOS_ENGINE_API DirectionalShadowCaster { CUBOS_REFLECT; diff --git a/engine/include/cubos/engine/render/shadows/plugin.hpp b/engine/include/cubos/engine/render/shadows/casters/plugin.hpp similarity index 50% rename from engine/include/cubos/engine/render/shadows/plugin.hpp rename to engine/include/cubos/engine/render/shadows/casters/plugin.hpp index 7a4dd29cf7..0f875c7404 100644 --- a/engine/include/cubos/engine/render/shadows/plugin.hpp +++ b/engine/include/cubos/engine/render/shadows/casters/plugin.hpp @@ -1,9 +1,9 @@ /// @dir -/// @brief @ref render-shadows-plugin plugin directory. +/// @brief @ref render-shadow-casters-plugin plugin directory. /// @file /// @brief Plugin entry point. -/// @ingroup render-shadows-plugin +/// @ingroup render-shadow-casters-plugin #pragma once @@ -12,12 +12,12 @@ namespace cubos::engine { - /// @defgroup render-shadows-plugin Shadows - /// @ingroup render-plugins + /// @defgroup render-shadow-casters-plugin Shadow casters + /// @ingroup render-shadows-plugins /// @brief Base interface plugin for shadows. /// @brief Plugin entry function. /// @param cubos @b Cubos main class. - /// @ingroup render-shadows-plugin - CUBOS_ENGINE_API void shadowsPlugin(Cubos& cubos); + /// @ingroup render-shadow-casters-plugin + CUBOS_ENGINE_API void shadowCastersPlugin(Cubos& cubos); } // namespace cubos::engine diff --git a/engine/include/cubos/engine/render/shadows/point_caster.hpp b/engine/include/cubos/engine/render/shadows/casters/point_caster.hpp similarity index 74% rename from engine/include/cubos/engine/render/shadows/point_caster.hpp rename to engine/include/cubos/engine/render/shadows/casters/point_caster.hpp index d09729fc5c..b079c3d7d1 100644 --- a/engine/include/cubos/engine/render/shadows/point_caster.hpp +++ b/engine/include/cubos/engine/render/shadows/casters/point_caster.hpp @@ -1,18 +1,18 @@ /// @file /// @brief Component @ref cubos::engine::PointShadowCaster. -/// @ingroup render-shadows-plugin +/// @ingroup render-shadow-casters-plugin #pragma once #include #include -#include +#include namespace cubos::engine { /// @brief Component which enables shadow casting on a point light. - /// @ingroup render-shadows-plugin + /// @ingroup render-shadow-casters-plugin struct CUBOS_ENGINE_API PointShadowCaster { CUBOS_REFLECT; diff --git a/engine/include/cubos/engine/render/shadows/spot_caster.hpp b/engine/include/cubos/engine/render/shadows/casters/spot_caster.hpp similarity index 74% rename from engine/include/cubos/engine/render/shadows/spot_caster.hpp rename to engine/include/cubos/engine/render/shadows/casters/spot_caster.hpp index cf01749078..a9610b22c4 100644 --- a/engine/include/cubos/engine/render/shadows/spot_caster.hpp +++ b/engine/include/cubos/engine/render/shadows/casters/spot_caster.hpp @@ -1,18 +1,18 @@ /// @file /// @brief Component @ref cubos::engine::SpotShadowCaster. -/// @ingroup render-shadows-plugin +/// @ingroup render-shadow-casters-plugin #pragma once #include #include -#include +#include namespace cubos::engine { /// @brief Component which enables shadow casting on a spot light. - /// @ingroup render-shadows-plugin + /// @ingroup render-shadow-casters-plugin struct CUBOS_ENGINE_API SpotShadowCaster { CUBOS_REFLECT; diff --git a/engine/include/cubos/engine/render/shadows/module.dox b/engine/include/cubos/engine/render/shadows/module.dox new file mode 100644 index 0000000000..1e1734195d --- /dev/null +++ b/engine/include/cubos/engine/render/shadows/module.dox @@ -0,0 +1,9 @@ +/// @dir +/// @brief @ref render-shadows-plugins module. + +namespace cubos::engine +{ + /// @defgroup render-shadows-plugins Shadows + /// @ingroup render-plugins + /// @brief Provides plugins for rendering shadows. +} // namespace cubos::engine diff --git a/engine/src/render/defaults/plugin.cpp b/engine/src/render/defaults/plugin.cpp index 58fb614b7e..187536279e 100644 --- a/engine/src/render/defaults/plugin.cpp +++ b/engine/src/render/defaults/plugin.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include #include @@ -14,9 +12,11 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include @@ -38,7 +38,7 @@ void cubos::engine::renderDefaultsPlugin(Cubos& cubos) cubos.plugin(lightsPlugin); cubos.plugin(cameraPlugin); cubos.plugin(shaderPlugin); - cubos.plugin(shadowsPlugin); + cubos.plugin(shadowCastersPlugin); cubos.plugin(hdrPlugin); cubos.plugin(gBufferPlugin); diff --git a/engine/src/render/deferred_shading/plugin.cpp b/engine/src/render/deferred_shading/plugin.cpp index 389c834011..c05a4f080c 100644 --- a/engine/src/render/deferred_shading/plugin.cpp +++ b/engine/src/render/deferred_shading/plugin.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -19,12 +18,14 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -188,7 +189,7 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) cubos.depends(lightsPlugin); cubos.depends(hdrPlugin); cubos.depends(transformPlugin); - cubos.depends(shadowsPlugin); + cubos.depends(shadowCastersPlugin); cubos.depends(shadowAtlasPlugin); cubos.depends(cascadedShadowMapsPlugin); @@ -216,7 +217,7 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) cubos.system("apply Deferred Shading to the GBuffer and output to the HDR texture") .tagged(deferredShadingTag) .call([](State& state, const Window& window, const RenderEnvironment& environment, - const ShadowAtlas& shadowAtlas, + const SpotShadowAtlas& spotShadowAtlas, const PointShadowAtlas& pointShadowAtlas, Query> directionalLights, Query> pointLights, @@ -360,60 +361,21 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) if (caster.contains()) { // Get light viewport - auto slot = shadowAtlas.slotsMap.at(caster.value().baseSettings.id); + auto slot = pointShadowAtlas.slotsMap.at(caster.value().baseSettings.id); - auto lightProj = - glm::perspective(glm::radians(90.0F), - (float(shadowAtlas.getPointAtlasSize().x) * slot->size.x) / - (float(shadowAtlas.getPointAtlasSize().y) * slot->size.y), - 0.1F, light.range); + auto lightProj = glm::perspective(glm::radians(90.0F), + (float(pointShadowAtlas.getSize().x) * slot->size.x) / + (float(pointShadowAtlas.getSize().y) * slot->size.y), + 0.1F, light.range); - for (int i = 0; i < 6; i++) - { - glm::mat4 lightView; - switch (i) - { - case 0: - lightView = glm::inverse(glm::scale( - lightLocalToWorld.mat, glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - case 1: - lightView = - glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(180.0F), - glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - case 2: - lightView = - glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(90.0F), - glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - case 3: - lightView = - glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(-90.0F), - glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - case 4: - lightView = - glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(90.0F), - glm::vec3(1.0F, 0.0F, 0.0F)), - glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - case 5: - lightView = - glm::inverse(glm::scale(glm::rotate(lightLocalToWorld.mat, glm::radians(-90.0F), - glm::vec3(1.0F, 0.0F, 0.0F)), - glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - default: - lightView = glm::inverse(glm::scale( - lightLocalToWorld.mat, glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - break; - } + std::vector lightViewMatrices; + core::geom::getCubeViewMatrices( + glm::scale(lightLocalToWorld.mat, glm::vec3(1.0F / lightLocalToWorld.worldScale())), + lightViewMatrices); - perLight.matrices[i] = lightProj * lightView; + for (unsigned long i = 0; i < 6; i++) + { + perLight.matrices[i] = lightProj * lightViewMatrices[i]; } perLight.shadowMapOffset = slot->offset; perLight.shadowMapSize = slot->size; @@ -441,17 +403,16 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) if (caster.contains()) { // Get light viewport - auto slot = shadowAtlas.slotsMap.at(caster.value().baseSettings.id); + auto slot = spotShadowAtlas.slotsMap.at(caster.value().baseSettings.id); // The light is actually facing the direction opposite to what's visible, so rotate it. auto lightView = glm::inverse(glm::scale( glm::rotate(lightLocalToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), glm::vec3(1.0F / lightLocalToWorld.worldScale()))); - auto lightProj = - glm::perspective(glm::radians(light.spotAngle), - (float(shadowAtlas.getSpotAtlasSize().x) * slot->size.x) / - (float(shadowAtlas.getSpotAtlasSize().y) * slot->size.y), - 0.1F, light.range); + auto lightProj = glm::perspective(glm::radians(light.spotAngle), + (float(spotShadowAtlas.getSize().x) * slot->size.x) / + (float(spotShadowAtlas.getSize().y) * slot->size.y), + 0.1F, light.range); perLight.matrix = lightProj * lightView; perLight.shadowMapOffset = slot->offset; perLight.shadowMapSize = slot->size; @@ -485,8 +446,8 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos) state.normalBP->bind(gBuffer.normal); state.albedoBP->bind(gBuffer.albedo); state.ssaoBP->bind(ssao.blurTexture); - state.spotShadowAtlasBP->bind(shadowAtlas.spotAtlas); - state.pointShadowAtlasBP->bind(shadowAtlas.pointAtlas); + state.spotShadowAtlasBP->bind(spotShadowAtlas.atlas); + state.pointShadowAtlasBP->bind(pointShadowAtlas.atlas); // directionalShadowMap needs to be bound even if it's null, or else errors may occur on some GPUs state.directionalShadowMapBP->bind(directionalShadowMap); state.directionalShadowMapBP->bind(state.directionalShadowSampler); diff --git a/engine/src/render/shadow_atlas/plugin.cpp b/engine/src/render/shadow_atlas/plugin.cpp deleted file mode 100644 index 7806184c48..0000000000 --- a/engine/src/render/shadow_atlas/plugin.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using cubos::core::io::Window; - -using cubos::engine::ShadowAtlas; -using cubos::engine::ShadowCaster; - -CUBOS_DEFINE_TAG(cubos::engine::createShadowAtlasTag); -CUBOS_DEFINE_TAG(cubos::engine::reserveShadowCastersTag); -CUBOS_DEFINE_TAG(cubos::engine::drawToShadowAtlasTag); - -static void reserveCasterSlot(std::vector>& slots, - std::map>& slotsMap, - ShadowCaster& casterBaseSettings, int& id) -{ - bool foundSlot = false; - casterBaseSettings.id = id++; - for (const auto& slot : slots) - { - if (slot->casterId == -1) - { - slot->casterId = casterBaseSettings.id; - slotsMap[casterBaseSettings.id] = slot; - foundSlot = true; - break; - } - } - if (!foundSlot) - { - // Subdivide largest slot, which is always the first - auto oldSlot = slots.at(0); - auto newSize = oldSlot->size / glm::vec2(2.0F); - - slots.erase(slots.begin()); - oldSlot->size = newSize; - slots.push_back(oldSlot); - - for (int i = 1; i < 4; i++) - { - // Intentional truncation of (i / 2) so that the result is always 0.0 or 1.0 - // NOLINTBEGIN(bugprone-integer-division) - auto newOffset = oldSlot->offset + - glm::vec2(newSize.x * static_cast(i % 2), newSize.y * static_cast(i / 2)); - // NOLINTEND(bugprone-integer-division) - auto newSlot = std::make_shared(newSize, newOffset, -1); - slots.push_back(newSlot); - } - - // Re-attempt - for (const auto& slot : slots) - { - if (slot->casterId == -1) - { - slot->casterId = casterBaseSettings.id; - slotsMap[casterBaseSettings.id] = slot; - break; - } - } - } -} - -void cubos::engine::shadowAtlasPlugin(Cubos& cubos) -{ - cubos.depends(shadowsPlugin); - cubos.depends(windowPlugin); - - cubos.resource(); - - cubos.tag(createShadowAtlasTag); - cubos.tag(reserveShadowCastersTag).after(createShadowAtlasTag); - cubos.tag(drawToShadowAtlasTag).after(reserveShadowCastersTag); - - cubos.system("create ShadowAtlas").tagged(createShadowAtlasTag).call([](const Window& window, ShadowAtlas& atlas) { - if (atlas.getSpotAtlasSize() != atlas.configSpotAtlasSize || - atlas.getPointAtlasSize() != atlas.configPointAtlasSize) - { - atlas.resize(window->renderDevice()); - - CUBOS_INFO("Resized ShadowAtlas to {}x{} (spot), {}x{} (point)", atlas.getSpotAtlasSize().x, - atlas.getSpotAtlasSize().y, atlas.getPointAtlasSize().x, atlas.getPointAtlasSize().y); - } - - // New frame, hint that the shadow atlas texture needs to be cleared. - atlas.cleared = false; - }); - - cubos.system("reserve space for shadow casters") - .tagged(reserveShadowCastersTag) - .call([](ShadowAtlas& atlas, Query spotCasters, Query pointCasters) { - atlas.spotAtlasSlots.clear(); - atlas.pointAtlasSlots.clear(); - atlas.slotsMap.clear(); - atlas.spotAtlasSlots.push_back( - std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); - atlas.pointAtlasSlots.push_back( - std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); - - int id = 1; - - for (auto [caster] : spotCasters) - { - reserveCasterSlot(atlas.spotAtlasSlots, atlas.slotsMap, caster.baseSettings, id); - } - - for (auto [caster] : pointCasters) - { - reserveCasterSlot(atlas.pointAtlasSlots, atlas.slotsMap, caster.baseSettings, id); - } - }); -} diff --git a/engine/src/render/shadow_atlas/shadow_atlas.cpp b/engine/src/render/shadow_atlas/shadow_atlas.cpp deleted file mode 100644 index fb28a46883..0000000000 --- a/engine/src/render/shadow_atlas/shadow_atlas.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include - -#include - -using cubos::core::gl::Texture2DArrayDesc; -using cubos::core::gl::Texture2DDesc; -using cubos::core::gl::TextureFormat; -using cubos::core::gl::Usage; - -CUBOS_REFLECT_IMPL(cubos::engine::ShadowAtlas) -{ - return core::ecs::TypeBuilder("cubos::engine::ShadowAtlas") - .withField("configSpotAtlasSize", &ShadowAtlas::configSpotAtlasSize) - .withField("configPointAtlasSize", &ShadowAtlas::configPointAtlasSize) - .withField("cleared", &ShadowAtlas::cleared) - .build(); -} - -glm::uvec2 cubos::engine::ShadowAtlas::getSpotAtlasSize() const -{ - return mSpotAtlasSize; -} - -glm::uvec2 cubos::engine::ShadowAtlas::getPointAtlasSize() const -{ - return mPointAtlasSize; -} - -void cubos::engine::ShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) -{ - mSpotAtlasSize = configSpotAtlasSize; - mPointAtlasSize = configPointAtlasSize; - - // Prepare texture description. - Texture2DDesc desc{}; - desc.width = mSpotAtlasSize.x; - desc.height = mSpotAtlasSize.y; - desc.usage = Usage::Dynamic; - - // Create spot shadow atlas texture. - desc.format = TextureFormat::Depth32; - spotAtlas = rd.createTexture2D(desc); - - // Prepare texture array description. - Texture2DArrayDesc cubeDesc{}; - cubeDesc.width = mPointAtlasSize.x; - cubeDesc.height = mPointAtlasSize.y; - cubeDesc.size = 6; - cubeDesc.usage = Usage::Dynamic; - - // Create point shadow atlas texture. - cubeDesc.format = TextureFormat::Depth32; - pointAtlas = rd.createTexture2DArray(cubeDesc); -} diff --git a/engine/src/render/shadows/atlas/plugin.cpp b/engine/src/render/shadows/atlas/plugin.cpp new file mode 100644 index 0000000000..699a4f0904 --- /dev/null +++ b/engine/src/render/shadows/atlas/plugin.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using cubos::core::io::Window; + +using cubos::engine::PointShadowAtlas; +using cubos::engine::ShadowCaster; +using cubos::engine::ShadowMapSlot; +using cubos::engine::SpotShadowAtlas; + +CUBOS_DEFINE_TAG(cubos::engine::createShadowAtlasTag); +CUBOS_DEFINE_TAG(cubos::engine::reserveShadowCastersTag); +CUBOS_DEFINE_TAG(cubos::engine::drawToShadowAtlasTag); + +static void reserveCasterSlot(std::vector>& slots, + std::map>& slotsMap, ShadowCaster& casterBaseSettings, + int& id) +{ + bool foundSlot = false; + casterBaseSettings.id = id++; + for (const auto& slot : slots) + { + if (slot->casterId == -1) + { + slot->casterId = casterBaseSettings.id; + slotsMap[casterBaseSettings.id] = slot; + foundSlot = true; + break; + } + } + if (!foundSlot) + { + // Subdivide largest slot, which is always the first + auto oldSlot = slots.at(0); + auto newSize = oldSlot->size / glm::vec2(2.0F); + + slots.erase(slots.begin()); + oldSlot->size = newSize; + slots.push_back(oldSlot); + + for (int i = 1; i < 4; i++) + { + // Intentional truncation of (i / 2) so that the result is always 0.0 or 1.0 + // NOLINTBEGIN(bugprone-integer-division) + auto newOffset = oldSlot->offset + + glm::vec2(newSize.x * static_cast(i % 2), newSize.y * static_cast(i / 2)); + // NOLINTEND(bugprone-integer-division) + auto newSlot = std::make_shared(newSize, newOffset, -1); + slots.push_back(newSlot); + } + + // Re-attempt + for (const auto& slot : slots) + { + if (slot->casterId == -1) + { + slot->casterId = casterBaseSettings.id; + slotsMap[casterBaseSettings.id] = slot; + break; + } + } + } +} + +void cubos::engine::shadowAtlasPlugin(Cubos& cubos) +{ + cubos.depends(shadowCastersPlugin); + cubos.depends(windowPlugin); + + cubos.resource(); + cubos.resource(); + + cubos.tag(createShadowAtlasTag); + cubos.tag(reserveShadowCastersTag).after(createShadowAtlasTag); + cubos.tag(drawToShadowAtlasTag).after(reserveShadowCastersTag); + + cubos.system("create shadow atlases") + .tagged(createShadowAtlasTag) + .call([](const Window& window, SpotShadowAtlas& spotAtlas, PointShadowAtlas& pointAtlas) { + if (spotAtlas.getSize() != spotAtlas.configSize) + { + spotAtlas.resize(window->renderDevice()); + + CUBOS_INFO("Resized SpotShadowAtlas to {}x{}", spotAtlas.getSize().x, spotAtlas.getSize().y); + } + if (pointAtlas.getSize() != pointAtlas.configSize) + { + pointAtlas.resize(window->renderDevice()); + + CUBOS_INFO("Resized PointShadowAtlas to {}x{}", pointAtlas.getSize().x, pointAtlas.getSize().y); + } + + // New frame, hint that the shadow atlas texture needs to be cleared. + spotAtlas.cleared = false; + pointAtlas.cleared = false; + }); + + cubos.system("reserve space for shadow casters") + .tagged(reserveShadowCastersTag) + .call([](SpotShadowAtlas& spotAtlas, PointShadowAtlas& pointAtlas, Query spotCasters, + Query pointCasters) { + spotAtlas.slots.clear(); + pointAtlas.slots.clear(); + spotAtlas.slotsMap.clear(); + pointAtlas.slotsMap.clear(); + spotAtlas.slots.push_back( + std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); + pointAtlas.slots.push_back( + std::make_shared(glm::vec2(1.0F, 1.0F), glm::vec2(0.0F, 0.0F), -1)); + + int id = 1; + + for (auto [caster] : spotCasters) + { + reserveCasterSlot(spotAtlas.slots, spotAtlas.slotsMap, caster.baseSettings, id); + } + + for (auto [caster] : pointCasters) + { + reserveCasterSlot(pointAtlas.slots, pointAtlas.slotsMap, caster.baseSettings, id); + } + }); +} diff --git a/engine/src/render/shadows/atlas/point_atlas.cpp b/engine/src/render/shadows/atlas/point_atlas.cpp new file mode 100644 index 0000000000..77d533be7b --- /dev/null +++ b/engine/src/render/shadows/atlas/point_atlas.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include + +using cubos::core::gl::Texture2DArrayDesc; +using cubos::core::gl::TextureFormat; +using cubos::core::gl::Usage; + +CUBOS_REFLECT_IMPL(cubos::engine::PointShadowAtlas) +{ + return core::ecs::TypeBuilder("cubos::engine::PointShadowAtlas") + .withField("configSize", &PointShadowAtlas::configSize) + .withField("cleared", &PointShadowAtlas::cleared) + .build(); +} + +glm::uvec2 cubos::engine::PointShadowAtlas::getSize() const +{ + return mSize; +} + +void cubos::engine::PointShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) +{ + mSize = configSize; + + // Prepare texture array description. + Texture2DArrayDesc cubeDesc{}; + cubeDesc.width = mSize.x; + cubeDesc.height = mSize.y; + cubeDesc.size = 6; + cubeDesc.usage = Usage::Dynamic; + + // Create point shadow atlas texture. + cubeDesc.format = TextureFormat::Depth32; + atlas = rd.createTexture2DArray(cubeDesc); +} diff --git a/engine/src/render/shadows/atlas/spot_atlas.cpp b/engine/src/render/shadows/atlas/spot_atlas.cpp new file mode 100644 index 0000000000..43dc76e829 --- /dev/null +++ b/engine/src/render/shadows/atlas/spot_atlas.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#include + +using cubos::core::gl::Texture2DDesc; +using cubos::core::gl::TextureFormat; +using cubos::core::gl::Usage; + +CUBOS_REFLECT_IMPL(cubos::engine::SpotShadowAtlas) +{ + return core::ecs::TypeBuilder("cubos::engine::SpotShadowAtlas") + .withField("configSize", &SpotShadowAtlas::configSize) + .withField("cleared", &SpotShadowAtlas::cleared) + .build(); +} + +glm::uvec2 cubos::engine::SpotShadowAtlas::getSize() const +{ + return mSize; +} + +void cubos::engine::SpotShadowAtlas::resize(cubos::core::gl::RenderDevice& rd) +{ + mSize = configSize; + + // Prepare texture description. + Texture2DDesc desc{}; + desc.width = mSize.x; + desc.height = mSize.y; + desc.usage = Usage::Dynamic; + + // Create spot shadow atlas texture. + desc.format = TextureFormat::Depth32; + atlas = rd.createTexture2D(desc); +} diff --git a/engine/src/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.cpp b/engine/src/render/shadows/atlas_rasterizer/atlas_rasterizer.cpp similarity index 71% rename from engine/src/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.cpp rename to engine/src/render/shadows/atlas_rasterizer/atlas_rasterizer.cpp index 641424c087..95fcf49f3b 100644 --- a/engine/src/render/shadow_atlas_rasterizer/shadow_atlas_rasterizer.cpp +++ b/engine/src/render/shadows/atlas_rasterizer/atlas_rasterizer.cpp @@ -1,6 +1,6 @@ #include -#include +#include CUBOS_REFLECT_IMPL(cubos::engine::ShadowAtlasRasterizer) { diff --git a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp b/engine/src/render/shadows/atlas_rasterizer/plugin.cpp similarity index 71% rename from engine/src/render/shadow_atlas_rasterizer/plugin.cpp rename to engine/src/render/shadows/atlas_rasterizer/plugin.cpp index 568a3cfa36..3143f440ad 100644 --- a/engine/src/render/shadow_atlas_rasterizer/plugin.cpp +++ b/engine/src/render/shadows/atlas_rasterizer/plugin.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -10,13 +11,14 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -34,6 +36,7 @@ namespace { glm::mat4 lightViewProj; }; + struct PerSceneCube { glm::mat4 lightViewProj[6]; @@ -121,14 +124,14 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) cubos.depends(lightsPlugin); cubos.depends(transformPlugin); cubos.depends(renderVoxelsPlugin); - cubos.depends(shadowsPlugin); + cubos.depends(shadowCastersPlugin); cubos.depends(shadowAtlasPlugin); cubos.uninitResource(); cubos.resource(); - cubos.startupSystem("setup ShadowAtlas Rasterizer") + cubos.startupSystem("setup Shadow Atlas Rasterizer") .tagged(assetsTag) .after(windowInitTag) .after(renderMeshPoolInitTag) @@ -142,39 +145,46 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) rd.createShaderPipeline(vsCube, gsCube, ps), pool.vertexBuffer()); }); - cubos.system("rasterize to ShadowAtlas") + cubos.system("rasterize to shadow atlases") .tagged(drawToShadowAtlasTag) - .call([](State& state, const Window& window, const RenderMeshPool& pool, ShadowAtlas& atlas, - ShadowAtlasRasterizer& rasterizer, + .call([](State& state, const Window& window, const RenderMeshPool& pool, SpotShadowAtlas& spotAtlas, + PointShadowAtlas& pointAtlas, ShadowAtlasRasterizer& rasterizer, Query spotLights, Query pointLights, Query meshes) { auto& rd = window->renderDevice(); // Check if we need to recreate the framebuffers. - if (rasterizer.atlas != atlas.spotAtlas) + if (rasterizer.spotAtlas != spotAtlas.atlas) { // Store textures so we can check if they change in the next frame. - rasterizer.atlas = atlas.spotAtlas; + rasterizer.spotAtlas = spotAtlas.atlas; - // Create the framebuffers. + // Create the framebuffer. FramebufferDesc desc{}; desc.targetCount = 0; - desc.depthStencil.setTexture2DTarget(atlas.spotAtlas); + desc.depthStencil.setTexture2DTarget(spotAtlas.atlas); rasterizer.spotAtlasFramebuffer = rd.createFramebuffer(desc); + CUBOS_INFO("Recreated ShadowAtlasRasterizer's spot atlas framebuffer"); + } + if (rasterizer.pointAtlas != pointAtlas.atlas) + { + // Store textures so we can check if they change in the next frame. + rasterizer.pointAtlas = pointAtlas.atlas; + + // Create the framebuffer. FramebufferDesc cubeDesc{}; cubeDesc.targetCount = 0; - cubeDesc.depthStencil.setTexture2DArrayTarget(atlas.pointAtlas); + cubeDesc.depthStencil.setTexture2DArrayTarget(pointAtlas.atlas); rasterizer.pointAtlasFramebuffer = rd.createFramebuffer(cubeDesc); - CUBOS_INFO("Recreated ShadowAtlasRasterizer's framebuffers"); + CUBOS_INFO("Recreated ShadowAtlasRasterizer's point atlas framebuffer"); } // Bind the spot atlas framebuffer and set the viewport. rd.setFramebuffer(rasterizer.spotAtlasFramebuffer); - rd.setViewport(0, 0, static_cast(atlas.getSpotAtlasSize().x), - static_cast(atlas.getSpotAtlasSize().y)); + rd.setViewport(0, 0, static_cast(spotAtlas.getSize().x), static_cast(spotAtlas.getSize().y)); // Set the raster and depth-stencil states. rd.setRasterState(state.rasterState); @@ -182,15 +192,16 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) rd.setDepthStencilState(state.depthStencilState); // Clear spot atlas - if (!atlas.cleared) + if (!spotAtlas.cleared) { rd.clearDepth(1.0F); + spotAtlas.cleared = true; } for (auto [caster, light, localToWorld] : spotLights) { // Get light viewport - auto slot = atlas.slotsMap.at(caster.baseSettings.id); + auto slot = spotAtlas.slotsMap.at(caster.baseSettings.id); // Send the PerScene data to the GPU. // The light is actually facing the direction opposite to what's visible, so rotate it. @@ -198,21 +209,21 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) glm::scale(glm::rotate(localToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), glm::vec3(1.0F / localToWorld.worldScale()))); auto proj = glm::perspective(glm::radians(light.spotAngle), - (float(atlas.getSpotAtlasSize().x) * slot->size.x) / - (float(atlas.getSpotAtlasSize().y) * slot->size.y), + (float(spotAtlas.getSize().x) * slot->size.x) / + (float(spotAtlas.getSize().y) * slot->size.y), 0.1F, light.range); PerScene perScene{.lightViewProj = proj * view}; state.perSceneCB->fill(&perScene, sizeof(perScene)); // Set the viewport. - rd.setViewport(static_cast(slot->offset.x * float(atlas.getSpotAtlasSize().x)), - static_cast(slot->offset.y * float(atlas.getSpotAtlasSize().y)), - static_cast(slot->size.x * float(atlas.getSpotAtlasSize().x)), - static_cast(slot->size.y * float(atlas.getSpotAtlasSize().y))); - rd.setScissor(static_cast(slot->offset.x * float(atlas.getSpotAtlasSize().x)), - static_cast(slot->offset.y * float(atlas.getSpotAtlasSize().y)), - static_cast(slot->size.x * float(atlas.getSpotAtlasSize().x)), - static_cast(slot->size.y * float(atlas.getSpotAtlasSize().y))); + rd.setViewport(static_cast(slot->offset.x * float(spotAtlas.getSize().x)), + static_cast(slot->offset.y * float(spotAtlas.getSize().y)), + static_cast(slot->size.x * float(spotAtlas.getSize().x)), + static_cast(slot->size.y * float(spotAtlas.getSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(spotAtlas.getSize().x)), + static_cast(slot->offset.y * float(spotAtlas.getSize().y)), + static_cast(slot->size.x * float(spotAtlas.getSize().x)), + static_cast(slot->size.y * float(spotAtlas.getSize().y))); // Bind the shader, vertex array and uniform buffer. rd.setShaderPipeline(state.pipeline); rd.setVertexArray(state.vertexArray); @@ -239,77 +250,44 @@ void cubos::engine::shadowAtlasRasterizerPlugin(Cubos& cubos) // Bind the point atlas framebuffer and set the viewport. rd.setFramebuffer(rasterizer.pointAtlasFramebuffer); - rd.setViewport(0, 0, static_cast(atlas.getPointAtlasSize().x), - static_cast(atlas.getPointAtlasSize().y)); + rd.setViewport(0, 0, static_cast(pointAtlas.getSize().x), static_cast(pointAtlas.getSize().y)); // Clear point atlas - if (!atlas.cleared) + if (!pointAtlas.cleared) { rd.clearDepth(1.0F); - atlas.cleared = true; + pointAtlas.cleared = true; } for (auto [caster, light, localToWorld] : pointLights) { // Get light viewport - auto slot = atlas.slotsMap.at(caster.baseSettings.id); + auto slot = pointAtlas.slotsMap.at(caster.baseSettings.id); // Set the viewport. - rd.setViewport(static_cast(slot->offset.x * float(atlas.getPointAtlasSize().x)), - static_cast(slot->offset.y * float(atlas.getPointAtlasSize().y)), - static_cast(slot->size.x * float(atlas.getPointAtlasSize().x)), - static_cast(slot->size.y * float(atlas.getPointAtlasSize().y))); - rd.setScissor(static_cast(slot->offset.x * float(atlas.getPointAtlasSize().x)), - static_cast(slot->offset.y * float(atlas.getPointAtlasSize().y)), - static_cast(slot->size.x * float(atlas.getPointAtlasSize().x)), - static_cast(slot->size.y * float(atlas.getPointAtlasSize().y))); + rd.setViewport(static_cast(slot->offset.x * float(pointAtlas.getSize().x)), + static_cast(slot->offset.y * float(pointAtlas.getSize().y)), + static_cast(slot->size.x * float(pointAtlas.getSize().x)), + static_cast(slot->size.y * float(pointAtlas.getSize().y))); + rd.setScissor(static_cast(slot->offset.x * float(pointAtlas.getSize().x)), + static_cast(slot->offset.y * float(pointAtlas.getSize().y)), + static_cast(slot->size.x * float(pointAtlas.getSize().x)), + static_cast(slot->size.y * float(pointAtlas.getSize().y))); // Send the PerScene data to the GPU. PerSceneCube perScene; auto proj = glm::perspective(glm::radians(90.0F), - (float(atlas.getPointAtlasSize().x) * slot->size.x) / - (float(atlas.getPointAtlasSize().y) * slot->size.y), + (float(pointAtlas.getSize().x) * slot->size.x) / + (float(pointAtlas.getSize().y) * slot->size.y), 0.1F, light.range); - for (int i = 0; i < 6; i++) - { - glm::mat4 view; - switch (i) - { - case 0: - view = glm::inverse(glm::scale(localToWorld.mat, glm::vec3(1.0F / localToWorld.worldScale()))); - break; - case 1: - view = glm::inverse( - glm::scale(glm::rotate(localToWorld.mat, glm::radians(180.0F), glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / localToWorld.worldScale()))); - break; - case 2: - view = glm::inverse( - glm::scale(glm::rotate(localToWorld.mat, glm::radians(90.0F), glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / localToWorld.worldScale()))); - break; - case 3: - view = glm::inverse( - glm::scale(glm::rotate(localToWorld.mat, glm::radians(-90.0F), glm::vec3(0.0F, 1.0F, 0.0F)), - glm::vec3(1.0F / localToWorld.worldScale()))); - break; - case 4: - view = glm::inverse( - glm::scale(glm::rotate(localToWorld.mat, glm::radians(90.0F), glm::vec3(1.0F, 0.0F, 0.0F)), - glm::vec3(1.0F / localToWorld.worldScale()))); - break; - case 5: - view = glm::inverse( - glm::scale(glm::rotate(localToWorld.mat, glm::radians(-90.0F), glm::vec3(1.0F, 0.0F, 0.0F)), - glm::vec3(1.0F / localToWorld.worldScale()))); - break; - default: - view = glm::inverse(glm::scale(localToWorld.mat, glm::vec3(1.0F / localToWorld.worldScale()))); - break; - } + std::vector viewMatrices; + core::geom::getCubeViewMatrices( + glm::scale(localToWorld.mat, glm::vec3(1.0F / localToWorld.worldScale())), viewMatrices); - perScene.lightViewProj[i] = proj * view; + for (unsigned long i = 0; i < 6; i++) + { + perScene.lightViewProj[i] = proj * viewMatrices[i]; } state.perSceneCubeCB->fill(&perScene, sizeof(perScene)); diff --git a/engine/src/render/cascaded_shadow_maps/plugin.cpp b/engine/src/render/shadows/cascaded/plugin.cpp similarity index 91% rename from engine/src/render/cascaded_shadow_maps/plugin.cpp rename to engine/src/render/shadows/cascaded/plugin.cpp index 1d0e7717da..8c1c387a8c 100644 --- a/engine/src/render/cascaded_shadow_maps/plugin.cpp +++ b/engine/src/render/shadows/cascaded/plugin.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include using cubos::core::io::Window; @@ -18,7 +18,7 @@ CUBOS_DEFINE_TAG(cubos::engine::drawToCascadedShadowMapsTag); void cubos::engine::cascadedShadowMapsPlugin(Cubos& cubos) { cubos.depends(cameraPlugin); - cubos.depends(shadowsPlugin); + cubos.depends(shadowCastersPlugin); cubos.depends(windowPlugin); cubos.tag(createCascadedShadowMapsTag); diff --git a/engine/src/render/cascaded_shadow_maps_rasterizer/plugin.cpp b/engine/src/render/shadows/cascaded_rasterizer/plugin.cpp similarity index 97% rename from engine/src/render/cascaded_shadow_maps_rasterizer/plugin.cpp rename to engine/src/render/shadows/cascaded_rasterizer/plugin.cpp index 2d9c0dcfb9..1dc5d32a85 100644 --- a/engine/src/render/cascaded_shadow_maps_rasterizer/plugin.cpp +++ b/engine/src/render/shadows/cascaded_rasterizer/plugin.cpp @@ -6,8 +6,6 @@ #include #include #include -#include -#include #include #include #include @@ -17,8 +15,10 @@ #include #include #include -#include -#include +#include +#include +#include +#include #include #include #include @@ -109,7 +109,7 @@ void cubos::engine::cascadedShadowMapsRasterizerPlugin(Cubos& cubos) cubos.depends(lightsPlugin); cubos.depends(transformPlugin); cubos.depends(renderVoxelsPlugin); - cubos.depends(shadowsPlugin); + cubos.depends(shadowCastersPlugin); cubos.depends(cascadedShadowMapsPlugin); cubos.depends(cameraPlugin); cubos.depends(gBufferPlugin); diff --git a/engine/src/render/shadows/caster.cpp b/engine/src/render/shadows/casters/caster.cpp similarity index 89% rename from engine/src/render/shadows/caster.cpp rename to engine/src/render/shadows/casters/caster.cpp index e388256a99..4a86064982 100644 --- a/engine/src/render/shadows/caster.cpp +++ b/engine/src/render/shadows/casters/caster.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include CUBOS_REFLECT_IMPL(cubos::engine::ShadowCaster) { diff --git a/engine/src/render/shadows/directional_caster.cpp b/engine/src/render/shadows/casters/directional_caster.cpp similarity index 97% rename from engine/src/render/shadows/directional_caster.cpp rename to engine/src/render/shadows/casters/directional_caster.cpp index 4eabad2fa4..2220a1bee2 100644 --- a/engine/src/render/shadows/directional_caster.cpp +++ b/engine/src/render/shadows/casters/directional_caster.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using cubos::core::gl::Texture2DArrayDesc; using cubos::core::gl::TextureFormat; diff --git a/engine/src/render/shadows/casters/plugin.cpp b/engine/src/render/shadows/casters/plugin.cpp new file mode 100644 index 0000000000..8ced408774 --- /dev/null +++ b/engine/src/render/shadows/casters/plugin.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include + +void cubos::engine::shadowCastersPlugin(Cubos& cubos) +{ + cubos.component(); + cubos.component(); + cubos.component(); +} diff --git a/engine/src/render/shadows/point_caster.cpp b/engine/src/render/shadows/casters/point_caster.cpp similarity index 85% rename from engine/src/render/shadows/point_caster.cpp rename to engine/src/render/shadows/casters/point_caster.cpp index 09a02e4a94..8ed34ffc1d 100644 --- a/engine/src/render/shadows/point_caster.cpp +++ b/engine/src/render/shadows/casters/point_caster.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include CUBOS_REFLECT_IMPL(cubos::engine::PointShadowCaster) { diff --git a/engine/src/render/shadows/spot_caster.cpp b/engine/src/render/shadows/casters/spot_caster.cpp similarity index 85% rename from engine/src/render/shadows/spot_caster.cpp rename to engine/src/render/shadows/casters/spot_caster.cpp index 94c3c000af..8d00dd6083 100644 --- a/engine/src/render/shadows/spot_caster.cpp +++ b/engine/src/render/shadows/casters/spot_caster.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include CUBOS_REFLECT_IMPL(cubos::engine::SpotShadowCaster) { diff --git a/engine/src/render/shadows/plugin.cpp b/engine/src/render/shadows/plugin.cpp deleted file mode 100644 index 739df07a30..0000000000 --- a/engine/src/render/shadows/plugin.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include -#include - -void cubos::engine::shadowsPlugin(Cubos& cubos) -{ - cubos.component(); - cubos.component(); - cubos.component(); -} From cf691245bdbaca73b82b15ad49c787865c8617cd Mon Sep 17 00:00:00 2001 From: tomas7770 <77364520+tomas7770@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:44:09 +0000 Subject: [PATCH 9/9] fix(rendering): reduce max number of point lights Uniform PerScene is becoming larger than the max limit in some GPUs --- engine/assets/render/deferred_shading.fs | 2 +- engine/src/render/deferred_shading/plugin.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/assets/render/deferred_shading.fs b/engine/assets/render/deferred_shading.fs index 03ea366243..019ecfcaa9 100644 --- a/engine/assets/render/deferred_shading.fs +++ b/engine/assets/render/deferred_shading.fs @@ -72,7 +72,7 @@ layout(std140) uniform PerScene // Lights data. DirectionalLight directionalLights[16]; - PointLight pointLights[128]; + PointLight pointLights[64]; SpotLight spotLights[128]; uint numDirectionalLights; diff --git a/engine/src/render/deferred_shading/plugin.cpp b/engine/src/render/deferred_shading/plugin.cpp index c05a4f080c..9537ce7758 100644 --- a/engine/src/render/deferred_shading/plugin.cpp +++ b/engine/src/render/deferred_shading/plugin.cpp @@ -108,7 +108,7 @@ namespace glm::vec4 ambientLight; PerDirectionalLight directionalLights[16]; - PerPointLight pointLights[128]; + PerPointLight pointLights[64]; PerSpotLight spotLights[128]; glm::uint numDirectionalLights{0};