2025-05-06 19:36:43 +02:00
|
|
|
#include "ShadowPass.h"
|
2025-07-14 21:10:07 +02:00
|
|
|
#include "Graphics/Enums.h"
|
2025-05-06 19:36:43 +02:00
|
|
|
#include "Graphics/Graphics.h"
|
2025-07-14 21:10:07 +02:00
|
|
|
#include "Graphics/Initializer.h"
|
2025-05-06 19:36:43 +02:00
|
|
|
#include "Graphics/Shader.h"
|
2025-08-14 11:06:43 +02:00
|
|
|
#include "Math/Matrix.h"
|
2025-07-14 21:10:07 +02:00
|
|
|
#include <glm/ext/matrix_transform.hpp>
|
|
|
|
|
#include <glm/matrix.hpp>
|
2025-05-06 19:36:43 +02:00
|
|
|
|
|
|
|
|
using namespace Seele;
|
|
|
|
|
|
|
|
|
|
ShadowPass::ShadowPass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics), scene(scene) {
|
|
|
|
|
shadowLayout = graphics->createPipelineLayout("ShadowLayout");
|
|
|
|
|
shadowLayout->addDescriptorLayout(viewParamsLayout);
|
|
|
|
|
shadowLayout->addPushConstants(Gfx::SePushConstantRange{
|
|
|
|
|
.stageFlags = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT,
|
|
|
|
|
.offset = 0,
|
|
|
|
|
.size = sizeof(VertexData::DrawCallOffsets),
|
|
|
|
|
});
|
|
|
|
|
if (graphics->supportMeshShading()) {
|
|
|
|
|
graphics->getShaderCompiler()->registerRenderPass("ShadowPass", Gfx::PassConfig{
|
|
|
|
|
.baseLayout = shadowLayout,
|
|
|
|
|
.taskFile = "DrawListTask",
|
|
|
|
|
.mainFile = "DrawListMesh",
|
|
|
|
|
.hasFragmentShader = false,
|
|
|
|
|
.useMeshShading = true,
|
|
|
|
|
.hasTaskShader = true,
|
|
|
|
|
.useMaterial = false,
|
|
|
|
|
.useVisibility = false,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
graphics->getShaderCompiler()->registerRenderPass("ShadowPass", Gfx::PassConfig{
|
|
|
|
|
.baseLayout = shadowLayout,
|
|
|
|
|
.taskFile = "",
|
|
|
|
|
.mainFile = "LegacyPass",
|
|
|
|
|
.hasFragmentShader = false,
|
|
|
|
|
.useMeshShading = false,
|
|
|
|
|
.hasTaskShader = false,
|
|
|
|
|
.useMaterial = false,
|
|
|
|
|
.useVisibility = false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShadowPass::~ShadowPass() {}
|
|
|
|
|
|
2025-07-14 21:10:07 +02:00
|
|
|
void ShadowPass::beginFrame(const Component::Camera& camera, const Component::Transform& transform) {
|
|
|
|
|
float cascadeSplits[NUM_CASCADES];
|
2025-08-23 18:09:01 +02:00
|
|
|
float splitDepths[NUM_CASCADES];
|
2025-07-14 21:10:07 +02:00
|
|
|
|
|
|
|
|
float nearClip = camera.nearPlane;
|
|
|
|
|
float farClip = camera.farPlane;
|
2025-09-27 21:50:01 +02:00
|
|
|
float clipRange = nearClip - farClip;
|
2025-07-14 21:10:07 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
float minZ = farClip;
|
|
|
|
|
float maxZ = nearClip;
|
2025-07-14 21:10:07 +02:00
|
|
|
|
|
|
|
|
float range = maxZ - minZ;
|
|
|
|
|
float ratio = maxZ / minZ;
|
|
|
|
|
constexpr float cascadeSplitLambda = 0.95f;
|
|
|
|
|
for (uint32 i = 0; i < NUM_CASCADES; ++i) {
|
|
|
|
|
float p = (i + 1) / static_cast<float>(NUM_CASCADES);
|
|
|
|
|
float log = minZ * std::pow(ratio, p);
|
|
|
|
|
float uniform = minZ + range * p;
|
|
|
|
|
float d = cascadeSplitLambda * (log - uniform) + uniform;
|
2025-09-27 21:50:01 +02:00
|
|
|
cascadeSplits[i] = (d - farClip) / clipRange;
|
|
|
|
|
splitDepths[i] = farClip + cascadeSplits[i] * clipRange;
|
2025-08-10 19:16:55 +02:00
|
|
|
cascades[i].viewParams.clear();
|
2025-07-14 21:10:07 +02:00
|
|
|
}
|
2025-08-23 18:09:01 +02:00
|
|
|
cascadeSplitsBuffer->updateContents(0, sizeof(float) * NUM_CASCADES, splitDepths);
|
2025-07-14 21:10:07 +02:00
|
|
|
|
|
|
|
|
updateViewParameters(camera, transform);
|
2025-08-23 18:09:01 +02:00
|
|
|
Matrix4 invCam = viewParams.inverseViewProjectionMatrix;
|
2025-09-27 21:50:01 +02:00
|
|
|
float lastSplitDist = 0.0;
|
|
|
|
|
for (uint32 i = 0; i < NUM_CASCADES; ++i) {
|
|
|
|
|
float splitDist = cascadeSplits[i];
|
2025-07-14 21:10:07 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
Array<Vector> frustumCorners = {
|
|
|
|
|
Vector(-1.0f, 1.0f, 0.0f), Vector(1.0f, 1.0f, 0.0f), Vector(1.0f, -1.0f, 0.0f), Vector(-1.0f, -1.0f, 0.0f),
|
|
|
|
|
Vector(-1.0f, 1.0f, 1.0f), Vector(1.0f, 1.0f, 1.0f), Vector(1.0f, -1.0f, 1.0f), Vector(-1.0f, -1.0f, 1.0f),
|
|
|
|
|
};
|
2025-08-23 18:09:01 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
for (auto& c : frustumCorners) {
|
|
|
|
|
Vector4 invCorner = invCam * Vector4(c, 1);
|
|
|
|
|
c = invCorner / invCorner.w;
|
|
|
|
|
}
|
|
|
|
|
for (uint32 j = 0; j < 4; j++) {
|
|
|
|
|
Vector dist = frustumCorners[j + 4] - frustumCorners[j];
|
|
|
|
|
frustumCorners[j + 4] = frustumCorners[j] + (dist * splitDist);
|
|
|
|
|
frustumCorners[j] = frustumCorners[j] + (dist * lastSplitDist);
|
|
|
|
|
}
|
2025-07-14 21:10:07 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
Vector frustumCenter = Vector(0);
|
|
|
|
|
for (uint32 j = 0; j < 8; j++) {
|
|
|
|
|
frustumCenter += frustumCorners[j];
|
|
|
|
|
}
|
|
|
|
|
frustumCenter /= 8.0f;
|
2025-07-14 21:10:07 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
float radius = 0.0f;
|
|
|
|
|
for (uint j = 0; j < 8; j++) {
|
|
|
|
|
float distance = glm::length(frustumCorners[j] - frustumCenter);
|
|
|
|
|
radius = glm::max(radius, distance);
|
|
|
|
|
}
|
|
|
|
|
radius = std::ceil(radius * 16.0f) / 16.0f;
|
2025-07-14 21:10:07 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
Vector maxExtents = Vector(radius);
|
|
|
|
|
Vector minExtents = -maxExtents;
|
2025-08-23 18:09:01 +02:00
|
|
|
|
2025-09-27 21:50:01 +02:00
|
|
|
for (uint32 s = 0; s < scene->getLightEnvironment()->getNumDirectionalLights(); ++s) {
|
2025-09-21 19:42:18 +02:00
|
|
|
Vector lightDir = glm::normalize(scene->getLightEnvironment()->getDirectionalLight(s).direction);
|
2025-08-10 12:28:50 +02:00
|
|
|
Vector cameraPos = frustumCenter - lightDir * -minExtents.z;
|
|
|
|
|
Matrix4 viewMatrix = glm::lookAt(cameraPos, frustumCenter, Vector(0, 1, 0));
|
2025-07-14 21:10:07 +02:00
|
|
|
Matrix4 projectionMatrix =
|
2025-08-14 11:06:43 +02:00
|
|
|
orthographicProjection(minExtents.x, maxExtents.x, minExtents.y, maxExtents.y, 0.0f, maxExtents.z - minExtents.z);
|
2025-07-14 21:10:07 +02:00
|
|
|
Matrix4 viewProjectionMatrix = projectionMatrix * viewMatrix;
|
2025-09-27 21:50:01 +02:00
|
|
|
viewParams = {
|
|
|
|
|
.viewMatrix = viewMatrix,
|
|
|
|
|
.inverseViewMatrix = glm::inverse(viewMatrix),
|
|
|
|
|
.projectionMatrix = projectionMatrix,
|
|
|
|
|
.inverseProjection = glm::inverse(projectionMatrix),
|
|
|
|
|
.viewProjectionMatrix = viewProjectionMatrix,
|
|
|
|
|
.inverseViewProjectionMatrix = glm::inverse(viewProjectionMatrix),
|
|
|
|
|
.cameraPosition_WS = Vector4(cameraPos, 1),
|
|
|
|
|
.cameraForward_WS = Vector4(frustumCenter - cameraPos, 0),
|
|
|
|
|
.screenDimensions = Vector2(maxExtents.x - minExtents.x, maxExtents.y - minExtents.y),
|
|
|
|
|
.invScreenDimensions = 1.0f / Vector2(maxExtents.x - minExtents.x, maxExtents.y - minExtents.y),
|
|
|
|
|
.frameIndex = Gfx::getCurrentFrameIndex(),
|
|
|
|
|
.time = (float)Gfx::getCurrentFrameTime(),
|
|
|
|
|
};
|
2025-07-14 21:10:07 +02:00
|
|
|
cascades[i].viewParams.add(createViewParamsSet());
|
2025-09-20 12:42:42 +02:00
|
|
|
cascades[i].lightSpaceBuffer->updateContents(0, sizeof(Matrix4), &viewProjectionMatrix);
|
2025-07-14 21:10:07 +02:00
|
|
|
}
|
2025-09-27 21:50:01 +02:00
|
|
|
lastSplitDist = cascadeSplits[i];
|
2025-07-14 21:10:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-05-06 19:36:43 +02:00
|
|
|
|
|
|
|
|
void ShadowPass::render() {
|
|
|
|
|
graphics->beginDebugRegion("ShadowPass");
|
|
|
|
|
Gfx::ShaderPermutation permutation = graphics->getShaderCompiler()->getTemplate("ShadowPass");
|
|
|
|
|
permutation.setDepthCulling(true);
|
|
|
|
|
permutation.setPositionOnly(true);
|
2025-07-14 21:10:07 +02:00
|
|
|
for (uint32 c = 0; c < NUM_CASCADES; ++c) {
|
|
|
|
|
graphics->beginDebugRegion("Cascade");
|
|
|
|
|
for (uint32 shadowIndex = 0; shadowIndex < cascades[c].shadowMaps->getNumLayers(); ++shadowIndex) {
|
|
|
|
|
Array<Gfx::ORenderCommand> commands;
|
|
|
|
|
renderPass = graphics->createRenderPass(
|
|
|
|
|
Gfx::RenderTargetLayout{
|
2025-08-08 22:29:54 +02:00
|
|
|
.depthAttachment = Gfx::RenderTargetAttachment(cascades[c].views[shadowIndex], Gfx::SE_IMAGE_LAYOUT_UNDEFINED,
|
|
|
|
|
Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
|
Gfx::SE_ATTACHMENT_LOAD_OP_CLEAR, Gfx::SE_ATTACHMENT_STORE_OP_STORE),
|
2025-05-06 19:36:43 +02:00
|
|
|
|
|
|
|
|
},
|
2025-07-14 21:10:07 +02:00
|
|
|
{
|
|
|
|
|
Gfx::SubPassDependency{
|
|
|
|
|
.srcSubpass = ~0U,
|
|
|
|
|
.dstSubpass = 0,
|
|
|
|
|
.srcStage = Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
|
|
|
.dstStage = Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
|
|
|
.srcAccess = Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
.dstAccess = Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
},
|
|
|
|
|
Gfx::SubPassDependency{
|
|
|
|
|
.srcSubpass = 0,
|
|
|
|
|
.dstSubpass = ~0U,
|
|
|
|
|
.srcStage = Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
|
|
|
.dstStage = Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
|
|
|
.srcAccess = Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
.dstAccess = Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
},
|
2025-05-06 19:36:43 +02:00
|
|
|
},
|
2025-07-14 21:10:07 +02:00
|
|
|
{
|
|
|
|
|
.size = {cascades[c].shadowMaps->getWidth(), cascades[c].shadowMaps->getHeight()},
|
|
|
|
|
.offset = {0, 0},
|
|
|
|
|
},
|
|
|
|
|
"Shadow");
|
|
|
|
|
graphics->beginRenderPass(renderPass);
|
|
|
|
|
for (VertexData* vertexData : VertexData::getList()) {
|
|
|
|
|
permutation.setVertexData(vertexData->getTypeName());
|
|
|
|
|
Gfx::PermutationId id(permutation);
|
2025-05-06 19:36:43 +02:00
|
|
|
|
2025-07-14 21:10:07 +02:00
|
|
|
Gfx::ORenderCommand command = graphics->createRenderCommand("ShadowRender");
|
2025-09-27 21:50:01 +02:00
|
|
|
command->setViewport(cascades[c].shadowViewport);
|
2025-05-06 19:36:43 +02:00
|
|
|
|
2025-07-14 21:10:07 +02:00
|
|
|
const Gfx::ShaderCollection* collection = graphics->getShaderCompiler()->findShaders(id);
|
|
|
|
|
constexpr float depthBiasConstant = -1.25f;
|
|
|
|
|
constexpr float depthBiasSlope = -1.75f;
|
|
|
|
|
if (graphics->supportMeshShading()) {
|
|
|
|
|
Gfx::MeshPipelineCreateInfo pipelineInfo = {
|
|
|
|
|
.taskShader = collection->taskShader,
|
|
|
|
|
.meshShader = collection->meshShader,
|
|
|
|
|
.fragmentShader = collection->fragmentShader,
|
|
|
|
|
.renderPass = renderPass,
|
|
|
|
|
.pipelineLayout = collection->pipelineLayout,
|
|
|
|
|
.rasterizationState =
|
|
|
|
|
{
|
2025-09-21 19:42:18 +02:00
|
|
|
.cullMode = Gfx::SE_CULL_MODE_NONE,
|
|
|
|
|
.depthBiasEnable = false,
|
2025-07-14 21:10:07 +02:00
|
|
|
.depthBiasConstantFactor = depthBiasConstant,
|
|
|
|
|
.depthBiasSlopeFactor = depthBiasSlope,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo));
|
|
|
|
|
command->bindPipeline(pipeline);
|
|
|
|
|
} else {
|
|
|
|
|
Gfx::LegacyPipelineCreateInfo pipelineInfo = {
|
|
|
|
|
.vertexShader = collection->vertexShader,
|
|
|
|
|
.fragmentShader = collection->fragmentShader,
|
|
|
|
|
.renderPass = renderPass,
|
|
|
|
|
.pipelineLayout = collection->pipelineLayout,
|
|
|
|
|
.rasterizationState =
|
|
|
|
|
{
|
|
|
|
|
.cullMode = Gfx::SE_CULL_MODE_FRONT_BIT,
|
|
|
|
|
.depthBiasEnable = true,
|
|
|
|
|
.depthBiasConstantFactor = depthBiasConstant,
|
|
|
|
|
.depthBiasSlopeFactor = depthBiasSlope,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo));
|
|
|
|
|
command->bindPipeline(pipeline);
|
|
|
|
|
}
|
2025-08-08 22:29:54 +02:00
|
|
|
command->bindDescriptor(
|
2025-08-10 19:16:55 +02:00
|
|
|
{cascades[c].viewParams[shadowIndex], vertexData->getVertexDataSet(), vertexData->getInstanceDataSet()});
|
2025-07-14 21:10:07 +02:00
|
|
|
VertexData::DrawCallOffsets offsets = {
|
|
|
|
|
.instanceOffset = 0,
|
2025-05-06 19:36:43 +02:00
|
|
|
};
|
2025-07-14 21:10:07 +02:00
|
|
|
command->pushConstants(Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT, 0,
|
|
|
|
|
sizeof(VertexData::DrawCallOffsets), &offsets);
|
|
|
|
|
if (graphics->supportMeshShading()) {
|
|
|
|
|
command->drawMesh((uint32)vertexData->getNumInstances(), 1, 1);
|
|
|
|
|
} else {
|
|
|
|
|
const auto& materials = vertexData->getMaterialData();
|
|
|
|
|
for (const auto& materialData : materials) {
|
|
|
|
|
// material not used for any active meshes, skip
|
|
|
|
|
if (materialData.instances.size() == 0)
|
|
|
|
|
continue;
|
|
|
|
|
for (const auto& drawCall : materialData.instances) {
|
|
|
|
|
command->bindIndexBuffer(vertexData->getIndexBuffer());
|
|
|
|
|
uint32 inst = drawCall.offsets.instanceOffset;
|
|
|
|
|
for (const auto& meshData : drawCall.instanceMeshData) {
|
|
|
|
|
// all meshlets of a mesh share the same indices offset
|
|
|
|
|
command->drawIndexed(meshData.indicesRange.size, 1, meshData.indicesRange.offset,
|
|
|
|
|
vertexData->getIndicesOffset(meshData.meshletRange.offset), inst++);
|
|
|
|
|
}
|
2025-05-06 19:36:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-14 21:10:07 +02:00
|
|
|
commands.add(std::move(command));
|
2025-05-06 19:36:43 +02:00
|
|
|
}
|
2025-07-14 21:10:07 +02:00
|
|
|
graphics->executeCommands(std::move(commands));
|
|
|
|
|
graphics->endRenderPass();
|
|
|
|
|
graphics->waitDeviceIdle();
|
2025-05-06 19:36:43 +02:00
|
|
|
}
|
2025-07-14 21:10:07 +02:00
|
|
|
graphics->endDebugRegion();
|
2025-05-06 19:36:43 +02:00
|
|
|
}
|
|
|
|
|
graphics->endDebugRegion();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ShadowPass::endFrame() {}
|
|
|
|
|
|
|
|
|
|
void ShadowPass::publishOutputs() {
|
2025-08-08 22:29:54 +02:00
|
|
|
cascadeSplitsBuffer = graphics->createUniformBuffer(UniformBufferCreateInfo{.sourceData =
|
2025-09-05 07:10:27 +02:00
|
|
|
{
|
|
|
|
|
.size = sizeof(float) * NUM_CASCADES,
|
|
|
|
|
.data = nullptr,
|
|
|
|
|
},
|
|
|
|
|
.name = "CascadeSplits"});
|
2025-08-08 22:29:54 +02:00
|
|
|
uint32 cascadeDim = SHADOW_MAP_SIZE;
|
2025-09-27 21:50:01 +02:00
|
|
|
for (uint32 c = 0; c < NUM_CASCADES; ++c) {
|
|
|
|
|
cascades[c].shadowMaps = graphics->createTexture2DArray(TextureCreateInfo{
|
2025-08-08 22:29:54 +02:00
|
|
|
.format = Gfx::SE_FORMAT_D32_SFLOAT,
|
|
|
|
|
.width = cascadeDim,
|
|
|
|
|
.height = cascadeDim,
|
|
|
|
|
.elements = 1, // TODO:
|
|
|
|
|
.usage = Gfx::SE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | Gfx::SE_IMAGE_USAGE_SAMPLED_BIT,
|
|
|
|
|
.name = "ShadowMapCascade",
|
|
|
|
|
});
|
2025-09-27 21:50:01 +02:00
|
|
|
cascades[c].lightSpaceBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{.sourceData =
|
2025-09-05 07:10:27 +02:00
|
|
|
{
|
|
|
|
|
.size = sizeof(Matrix4),
|
|
|
|
|
.data = nullptr,
|
|
|
|
|
},
|
|
|
|
|
.name = "LightSpaceBuffer"});
|
2025-09-27 21:50:01 +02:00
|
|
|
cascades[c].views.clear();
|
|
|
|
|
for (uint32 j = 0; j < cascades[c].shadowMaps->getNumLayers(); ++j) {
|
|
|
|
|
cascades[c].views.add(cascades[c].shadowMaps->createTextureView(0, 1, j, 1));
|
2025-08-08 22:29:54 +02:00
|
|
|
}
|
2025-09-27 21:50:01 +02:00
|
|
|
cascades[c].shadowViewport = graphics->createViewport(nullptr, ViewportCreateInfo{
|
|
|
|
|
.dimensions =
|
|
|
|
|
{
|
|
|
|
|
.size = {cascadeDim, cascadeDim},
|
|
|
|
|
.offset = {0, 0},
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-08-08 22:29:54 +02:00
|
|
|
cascadeDim /= 2;
|
2025-09-27 21:50:01 +02:00
|
|
|
resources->registerTextureOutput(fmt::format("SHADOWMAP_TEXTURE{0}", c), Gfx::PTexture2DArray(cascades[c].shadowMaps));
|
|
|
|
|
resources->registerBufferOutput(fmt::format("SHADOWMAP_LIGHTSPACE{0}", c), cascades[c].lightSpaceBuffer);
|
2025-08-08 22:29:54 +02:00
|
|
|
}
|
2025-09-05 07:10:27 +02:00
|
|
|
cascadeSplitsBuffer = graphics->createUniformBuffer(UniformBufferCreateInfo{.sourceData =
|
|
|
|
|
{
|
|
|
|
|
.size = sizeof(float) * NUM_CASCADES,
|
|
|
|
|
.data = nullptr,
|
|
|
|
|
},
|
|
|
|
|
.name = "CASCADE_SPLITS"});
|
2025-08-08 22:29:54 +02:00
|
|
|
resources->registerUniformOutput("SHADOWMAP_CASCADESPLITS", cascadeSplitsBuffer);
|
2025-09-27 21:50:01 +02:00
|
|
|
viewport = cascades[0].shadowViewport;
|
2025-05-06 19:36:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ShadowPass::createRenderPass() { cullingBuffer = resources->requestBuffer("CULLINGBUFFER"); }
|