feat: add spot light shadows with a tilled buffer
This commit is contained in:
@@ -70,6 +70,8 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
|
||||
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
|
||||
shadowLightDirection.reset();
|
||||
submittedLights.clear();
|
||||
spotShadows.clear();
|
||||
Renderer2D::GetInstance().Clear();
|
||||
}
|
||||
|
||||
@@ -126,15 +128,26 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Lights Buffer Flush", vkutil::labelColors::transfer);
|
||||
|
||||
uploadLights(sceneData.camera);
|
||||
lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice);
|
||||
}
|
||||
|
||||
const glm::mat4 lightViewProj = shadowLightDirection
|
||||
? shadowMapPipeline->calculateLightViewProj(
|
||||
? shadowMapPipeline->calculateDirectionalViewProj(
|
||||
*shadowLightDirection,
|
||||
sceneData.camera.GetPosition())
|
||||
: glm::mat4{1.0f};
|
||||
|
||||
std::array<GPUSpotShadow, ShadowMapPipeline::MAX_SPOT_SHADOWS> gpuSpotShadows{};
|
||||
std::ranges::copy(spotShadows, gpuSpotShadows.begin());
|
||||
|
||||
std::vector<glm::mat4> spotShadowViewProjs;
|
||||
spotShadowViewProjs.reserve(spotShadows.size());
|
||||
for (const GPUSpotShadow& spotShadow : spotShadows)
|
||||
{
|
||||
spotShadowViewProjs.push_back(spotShadow.lightViewProj);
|
||||
}
|
||||
|
||||
// TODO: there has to be a better way to do this
|
||||
const auto gpuSceneData = GPUSceneData{
|
||||
.view = sceneData.camera.GetViewMatrix(),
|
||||
@@ -156,6 +169,9 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
.shadowNormalOffset = shadowMapPipeline->getNormalOffset(),
|
||||
.padding = 0,
|
||||
.lightViewProj = lightViewProj,
|
||||
.spotShadowAtlasId = static_cast<std::uint32_t>(shadowMapPipeline->getSpotShadowAtlasId()),
|
||||
.spotShadowTilesPerRow = ShadowMapPipeline::SPOT_SHADOW_TILES_PER_ROW,
|
||||
.spotShadows = gpuSpotShadows,
|
||||
};
|
||||
|
||||
{
|
||||
@@ -212,12 +228,25 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Shadow Map");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Shadow Map", vkutil::labelColors::shadow);
|
||||
|
||||
shadowMapPipeline->draw(
|
||||
shadowMapPipeline->drawDirectionalShadow(
|
||||
cmd,
|
||||
*resources,
|
||||
sceneDataBuffer.getBuffer(),
|
||||
meshDrawCommands,
|
||||
sortedMeshDrawCommands);
|
||||
sortedMeshDrawCommands,
|
||||
lightViewProj);
|
||||
}
|
||||
|
||||
if (!spotShadowViewProjs.empty())
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Spot Shadows");
|
||||
vkutil::ScopedCmdLabel label(cmd, "Spot Shadows", vkutil::labelColors::shadow);
|
||||
|
||||
shadowMapPipeline->drawSpotShadows(
|
||||
cmd,
|
||||
*resources,
|
||||
meshDrawCommands,
|
||||
sortedMeshDrawCommands,
|
||||
spotShadowViewProjs);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -400,6 +429,8 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||
lightCullingPipeline.reset();
|
||||
shadowMapPipeline.reset();
|
||||
shadowLightDirection.reset();
|
||||
submittedLights.clear();
|
||||
spotShadows.clear();
|
||||
pendingMaterialUploads.clear();
|
||||
meshDrawCommands.clear();
|
||||
sortedMeshDrawCommands.clear();
|
||||
@@ -444,18 +475,46 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
|
||||
|
||||
void GameRenderer::submitLight(const Light& light)
|
||||
{
|
||||
Light submittedLight = light;
|
||||
submittedLights.push_back(light);
|
||||
}
|
||||
|
||||
// there's a single shadow map, the first shadow casting directional light gets it
|
||||
submittedLight.castShadows =
|
||||
light.castShadows && light.type == LightType::Directional && !shadowLightDirection;
|
||||
void GameRenderer::uploadLights(const Camera& camera)
|
||||
{
|
||||
const Frustum cameraFrustum = edge::createFrustumFromCamera(camera);
|
||||
|
||||
if (submittedLight.castShadows)
|
||||
for (const Light& light : submittedLights)
|
||||
{
|
||||
shadowLightDirection = light.direction;
|
||||
lightCullingPipeline->appendLight(light, assignShadowIndex(light, cameraFrustum));
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t GameRenderer::assignShadowIndex(const Light& light, const Frustum& cameraFrustum)
|
||||
{
|
||||
if (!light.castShadows)
|
||||
{
|
||||
return NO_SHADOW;
|
||||
}
|
||||
|
||||
lightCullingPipeline->appendLight(submittedLight);
|
||||
// there's a single directional shadow map, the first shadow casting directional light gets it
|
||||
if (light.type == LightType::Directional && !shadowLightDirection)
|
||||
{
|
||||
shadowLightDirection = light.direction;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (light.type == LightType::Spot && spotShadows.size() < ShadowMapPipeline::MAX_SPOT_SHADOWS)
|
||||
{
|
||||
const GPUSpotShadow spotShadow = ShadowMapPipeline::calculateSpotShadow(light);
|
||||
if (!edge::frustumsOverlap(cameraFrustum, spotShadow.lightViewProj))
|
||||
{
|
||||
return NO_SHADOW;
|
||||
}
|
||||
|
||||
spotShadows.push_back(spotShadow);
|
||||
return static_cast<std::uint32_t>(spotShadows.size() - 1);
|
||||
}
|
||||
|
||||
return NO_SHADOW;
|
||||
}
|
||||
|
||||
const GPUImage& GameRenderer::getDrawImage() const
|
||||
|
||||
Reference in New Issue
Block a user