Skip to content

Commit a5c0600

Browse files
committed
WIP: panels and shaders + shadows fix
- Remove useless panels, and made that we can individually reload shaders - A fix for shadows, not the solution but a fix for something else that I didn't know about and fixed it by accident
1 parent 087be28 commit a5c0600

8 files changed

Lines changed: 539 additions & 261 deletions

File tree

Core/Source/Lux/Renderer/SceneRenderer.cpp

Lines changed: 170 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <glm/gtc/type_ptr.hpp>
1313

1414
#include <algorithm>
15+
#include <cmath>
1516
#include <cstring>
1617
#include <limits>
1718

@@ -221,7 +222,7 @@ namespace Lux {
221222
{ ShaderDataType::Float2, "a_TexCoord" }
222223
};
223224

224-
// ── Shadow map (single directional, ortho projection) ─────────────────
225+
// ── Directional shadow maps ───────────────────────────────────────────
225226
{
226227
ImageSpecification shadowMapSpec;
227228
shadowMapSpec.DebugName = "ShadowMapArray";
@@ -234,35 +235,40 @@ namespace Lux {
234235
m_ShadowMapImage = Image2D::Create(shadowMapSpec);
235236
m_ShadowMapImage->RT_Invalidate();
236237

237-
FramebufferSpecification fbSpec;
238-
fbSpec.Width = 4096;
239-
fbSpec.Height = 4096;
240-
fbSpec.Attachments = { ImageFormat::Depth };
241-
fbSpec.DepthClearValue = 1.0f;
242-
fbSpec.DebugName = "ShadowMap";
243-
fbSpec.ExistingImage = m_ShadowMapImage;
244-
fbSpec.ExistingImageLayer = 0;
245-
246-
PipelineSpecification pipelineSpec;
247-
pipelineSpec.DebugName = "DirShadowMap";
248-
pipelineSpec.Shader = Renderer::GetShaderLibrary()->Get("DirShadowMap");
249-
pipelineSpec.TargetFramebuffer = Framebuffer::Create(fbSpec);
250-
pipelineSpec.Layout = vertexLayout;
251-
pipelineSpec.DepthOperator = DepthCompareOperator::LessOrEqual;
252-
pipelineSpec.BackfaceCulling = false; // avoid peter-panning
238+
Ref<Shader> shadowPassShader = Renderer::GetShaderLibrary()->Get("DirShadowMap");
239+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
240+
{
241+
FramebufferSpecification fbSpec;
242+
fbSpec.Width = 4096;
243+
fbSpec.Height = 4096;
244+
fbSpec.Attachments = { ImageFormat::Depth };
245+
fbSpec.DepthClearValue = 1.0f;
246+
fbSpec.DebugName = "ShadowMap-Cascade" + std::to_string(cascade);
247+
fbSpec.ExistingImage = m_ShadowMapImage;
248+
fbSpec.ExistingImageLayer = cascade;
249+
250+
PipelineSpecification pipelineSpec;
251+
pipelineSpec.DebugName = "DirShadowMap-Cascade" + std::to_string(cascade);
252+
pipelineSpec.Shader = shadowPassShader;
253+
pipelineSpec.TargetFramebuffer = Framebuffer::Create(fbSpec);
254+
pipelineSpec.Layout = vertexLayout;
255+
pipelineSpec.DepthOperator = DepthCompareOperator::LessOrEqual;
256+
pipelineSpec.BackfaceCulling = false; // avoid peter-panning
253257

254-
RenderPassSpecification rpSpec;
255-
rpSpec.DebugName = "ShadowMapPass";
256-
rpSpec.Pipeline = Pipeline::Create(pipelineSpec);
258+
RenderPassSpecification rpSpec;
259+
rpSpec.DebugName = "ShadowMapPass-Cascade" + std::to_string(cascade);
260+
rpSpec.Pipeline = Pipeline::Create(pipelineSpec);
257261

258-
m_ShadowMapPass = RenderPass::Create(rpSpec);
259-
m_ShadowMapPass->SetInput("ShadowData", m_UBSShadow);
260-
m_ShadowMapPass->SetInput("InstanceTransforms", m_SBSInstanceTransforms);
261-
m_ShadowMapPass->SetInput("ObjectIndexes", m_SBSObjectIndexes);
262-
LUX_CORE_VERIFY(m_ShadowMapPass->Validate());
263-
m_ShadowMapPass->Bake();
262+
m_ShadowMapPasses[cascade] = RenderPass::Create(rpSpec);
263+
m_ShadowMapPasses[cascade]->SetInput("ShadowData", m_UBSShadow);
264+
m_ShadowMapPasses[cascade]->SetInput("InstanceTransforms", m_SBSInstanceTransforms);
265+
m_ShadowMapPasses[cascade]->SetInput("ObjectIndexes", m_SBSObjectIndexes);
266+
LUX_CORE_VERIFY(m_ShadowMapPasses[cascade]->Validate());
267+
m_ShadowMapPasses[cascade]->Bake();
268+
}
264269

265-
m_ShadowPassMaterial = Material::Create(pipelineSpec.Shader, "ShadowPass");
270+
m_ShadowMapPass = m_ShadowMapPasses[0];
271+
m_ShadowPassMaterial = Material::Create(shadowPassShader, "ShadowPass");
266272
}
267273

268274
// ── Spot shadow atlas (single depth atlas) ───────────────────────────
@@ -1392,6 +1398,98 @@ namespace Lux {
13921398
m_SceneData.SkyboxLod = skyboxLod;
13931399
}
13941400

1401+
void SceneRenderer::CalculateCascades(CascadeData* cascades, const SceneRendererCamera& sceneCamera, const glm::vec3& lightDirection) const
1402+
{
1403+
const float nearClip = glm::max(sceneCamera.Near, 0.001f);
1404+
const float cameraFar = glm::max(sceneCamera.Far, nearClip + 0.001f);
1405+
const float shadowFar = glm::clamp(m_Options.MaxShadowDistance, nearClip + 0.001f, cameraFar);
1406+
const float cameraClipRange = cameraFar - nearClip;
1407+
const float shadowRange = shadowFar - nearClip;
1408+
const float ratio = shadowFar / nearClip;
1409+
1410+
float cascadeSplits[ShadowCascadeCount]{};
1411+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
1412+
{
1413+
const float p = (cascade + 1.0f) / static_cast<float>(ShadowCascadeCount);
1414+
const float logSplit = nearClip * std::pow(ratio, p);
1415+
const float uniformSplit = nearClip + shadowRange * p;
1416+
const float splitDistance = glm::mix(uniformSplit, logSplit, glm::clamp(m_Options.ShadowCascadeSplitLambda, 0.0f, 1.0f));
1417+
cascadeSplits[cascade] = (splitDistance - nearClip) / cameraClipRange;
1418+
}
1419+
cascadeSplits[ShadowCascadeCount - 1] = (shadowFar - nearClip) / cameraClipRange;
1420+
1421+
const glm::mat4 viewProjection = sceneCamera.Camera.GetUnReversedProjectionMatrix() * sceneCamera.ViewMatrix;
1422+
const glm::mat4 inverseViewProjection = glm::inverse(viewProjection);
1423+
const float shadowMapResolution = m_ShadowMapPass ? static_cast<float>(m_ShadowMapPass->GetTargetFramebuffer()->GetWidth()) : 4096.0f;
1424+
const glm::vec3 normalizedLightDirection = glm::normalize(lightDirection);
1425+
const glm::vec3 up = glm::abs(glm::dot(normalizedLightDirection, glm::vec3(0.0f, 1.0f, 0.0f))) < 0.99f
1426+
? glm::vec3(0.0f, 1.0f, 0.0f)
1427+
: glm::vec3(1.0f, 0.0f, 0.0f);
1428+
1429+
float lastSplitDist = 0.0f;
1430+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
1431+
{
1432+
const float splitDist = cascadeSplits[cascade];
1433+
1434+
glm::vec3 frustumCorners[8] =
1435+
{
1436+
{ -1.0f, 1.0f, 0.0f },
1437+
{ 1.0f, 1.0f, 0.0f },
1438+
{ 1.0f, -1.0f, 0.0f },
1439+
{ -1.0f, -1.0f, 0.0f },
1440+
{ -1.0f, 1.0f, 1.0f },
1441+
{ 1.0f, 1.0f, 1.0f },
1442+
{ 1.0f, -1.0f, 1.0f },
1443+
{ -1.0f, -1.0f, 1.0f },
1444+
};
1445+
1446+
for (glm::vec3& corner : frustumCorners)
1447+
{
1448+
const glm::vec4 worldCorner = inverseViewProjection * glm::vec4(corner, 1.0f);
1449+
corner = glm::vec3(worldCorner) / worldCorner.w;
1450+
}
1451+
1452+
for (uint32_t i = 0; i < 4; i++)
1453+
{
1454+
const glm::vec3 cornerRay = frustumCorners[i + 4] - frustumCorners[i];
1455+
frustumCorners[i + 4] = frustumCorners[i] + cornerRay * splitDist;
1456+
frustumCorners[i] = frustumCorners[i] + cornerRay * lastSplitDist;
1457+
}
1458+
1459+
glm::vec3 frustumCenter(0.0f);
1460+
for (const glm::vec3& corner : frustumCorners)
1461+
frustumCenter += corner;
1462+
frustumCenter /= 8.0f;
1463+
1464+
float radius = 0.0f;
1465+
for (const glm::vec3& corner : frustumCorners)
1466+
radius = glm::max(radius, glm::length(corner - frustumCenter));
1467+
radius = glm::max(std::ceil(radius * 16.0f) / 16.0f, 0.01f);
1468+
1469+
const glm::vec3 maxExtents(radius);
1470+
const glm::vec3 minExtents = -maxExtents;
1471+
glm::mat4 lightView = glm::lookAt(frustumCenter - normalizedLightDirection * radius, frustumCenter, up);
1472+
glm::mat4 lightProjection = glm::ortho(
1473+
minExtents.x, maxExtents.x,
1474+
minExtents.y, maxExtents.y,
1475+
glm::max(0.0f, m_Options.ShadowCascadeNearPlaneOffset),
1476+
maxExtents.z - minExtents.z + glm::max(0.0f, m_Options.ShadowCascadeFarPlaneOffset));
1477+
1478+
glm::mat4 shadowMatrix = lightProjection * lightView;
1479+
glm::vec4 shadowOrigin = (shadowMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) * shadowMapResolution * 0.5f;
1480+
glm::vec4 roundedOrigin = glm::round(shadowOrigin);
1481+
glm::vec4 roundOffset = (roundedOrigin - shadowOrigin) * (2.0f / shadowMapResolution);
1482+
roundOffset.z = 0.0f;
1483+
roundOffset.w = 0.0f;
1484+
lightProjection[3] += roundOffset;
1485+
1486+
cascades[cascade].SplitDepth = -(nearClip + splitDist * cameraClipRange);
1487+
cascades[cascade].ViewProj = lightProjection * lightView;
1488+
1489+
lastSplitDist = splitDist;
1490+
}
1491+
}
1492+
13951493
// ─────────────────────────────────────────────────────────────────────────
13961494
// BeginScene
13971495
// ─────────────────────────────────────────────────────────────────────────
@@ -1618,36 +1716,29 @@ namespace Lux {
16181716
});
16191717
}
16201718

1621-
// ── Directional shadow matrix ─────────────────────────────────────────
1622-
// Single ortho shadow map centred on the camera position.
1719+
// ── Directional shadow matrices ───────────────────────────────────────
16231720
{
16241721
const auto& dirLight = m_SceneData.SceneLightEnvironment.DirectionalLights[0];
16251722

16261723
if (dirLight.Intensity > 0.0f && dirLight.CastShadows)
16271724
{
1628-
const glm::vec3 lightDir = glm::normalize(dirLight.Direction);
1629-
const glm::vec3 camPos = glm::vec3(glm::inverse(camera.ViewMatrix)[3]);
1630-
1631-
// Pick an up vector that is not collinear with the light direction
1632-
const glm::vec3 up = glm::abs(glm::dot(lightDir, glm::vec3(0, 1, 0))) < 0.99f
1633-
? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0);
1634-
1635-
const glm::mat4 lightView = glm::lookAt(
1636-
camPos - lightDir * 150.0f,
1637-
camPos,
1638-
up);
1639-
1640-
const float halfSize = m_Options.MaxShadowDistance * 0.5f;
1641-
const glm::mat4 lightProj = glm::ortho(
1642-
-halfSize, halfSize,
1643-
-halfSize, halfSize,
1644-
-500.0f, 500.0f);
1645-
1646-
m_ShadowUB.ViewProjection[0] = lightProj * lightView;
1725+
CascadeData cascades[ShadowCascadeCount];
1726+
CalculateCascades(cascades, camera, dirLight.Direction);
1727+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
1728+
m_ShadowUB.ViewProjection[cascade] = cascades[cascade].ViewProj;
1729+
1730+
m_RendererDataUB.CascadeSplits = {
1731+
cascades[0].SplitDepth,
1732+
cascades[1].SplitDepth,
1733+
cascades[2].SplitDepth,
1734+
cascades[3].SplitDepth
1735+
};
16471736
}
16481737
else
16491738
{
1650-
m_ShadowUB.ViewProjection[0] = glm::mat4(1.0f);
1739+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
1740+
m_ShadowUB.ViewProjection[cascade] = glm::mat4(1.0f);
1741+
m_RendererDataUB.CascadeSplits = glm::vec4(-1000000.0f);
16511742
}
16521743

16531744
auto shadowData = m_ShadowUB;
@@ -1665,7 +1756,10 @@ namespace Lux {
16651756
m_RendererDataUB.LightSize = dirLight.LightSize;
16661757
m_RendererDataUB.MaxShadowDistance = m_Options.MaxShadowDistance;
16671758
m_RendererDataUB.ShadowFade = m_Options.ShadowFade;
1668-
m_RendererDataUB.CascadeSplits = glm::vec4(-1000000.0f);
1759+
m_RendererDataUB.CascadeFading = true;
1760+
m_RendererDataUB.CascadeTransitionFade = m_Options.ShadowCascadeTransitionFade;
1761+
m_RendererDataUB.ShowCascades = m_Options.ShowShadowCascades;
1762+
m_RendererDataUB.ShowLightComplexity = m_Options.ShowLightComplexity;
16691763
m_RendererDataUB.TilesCountX = m_LightTilesCountX;
16701764

16711765
auto rdData = m_RendererDataUB;
@@ -2129,35 +2223,41 @@ namespace Lux {
21292223
const auto& dirLight = m_SceneData.SceneLightEnvironment.DirectionalLights[0];
21302224
if (dirLight.Intensity <= 0.0f || !dirLight.CastShadows)
21312225
{
2132-
// Clear the shadow map so geometry doesn't sample stale data
2133-
Renderer::BeginRenderPass(m_CommandBuffer, m_ShadowMapPass, /*explicitClear=*/true);
2134-
Renderer::EndRenderPass(m_CommandBuffer);
2226+
// Clear every cascade so geometry doesn't sample stale data.
2227+
for (auto& shadowMapPass : m_ShadowMapPasses)
2228+
{
2229+
Renderer::BeginRenderPass(m_CommandBuffer, shadowMapPass, /*explicitClear=*/true);
2230+
Renderer::EndRenderPass(m_CommandBuffer);
2231+
}
21352232
return;
21362233
}
21372234

21382235
Renderer::BeginGPUPerfMarker(m_CommandBuffer, "ShadowMapPass");
2139-
Renderer::BeginRenderPass(m_CommandBuffer, m_ShadowMapPass, /*explicitClear=*/true);
2140-
2141-
for (auto& [key, dc] : m_StaticMeshShadowPassDrawList)
2236+
for (uint32_t cascade = 0; cascade < ShadowCascadeCount; cascade++)
21422237
{
2143-
auto it = m_ShadowMeshTransformMap.find(key);
2144-
if (it == m_ShadowMeshTransformMap.end()) continue;
2238+
Renderer::BeginRenderPass(m_CommandBuffer, m_ShadowMapPasses[cascade], /*explicitClear=*/true);
21452239

2146-
const auto& cascadeTmd = it->second.Cascade;
2147-
const uint32_t instCount = (uint32_t)cascadeTmd.ObjectIndices.size();
2148-
if (instCount == 0) continue;
2240+
for (auto& [key, dc] : m_StaticMeshShadowPassDrawList)
2241+
{
2242+
auto it = m_ShadowMeshTransformMap.find(key);
2243+
if (it == m_ShadowMeshTransformMap.end()) continue;
21492244

2150-
StaticDrawCommand drawCmd = dc;
2151-
drawCmd.InstanceCount = instCount;
2245+
const auto& cascadeTmd = it->second.Cascade;
2246+
const uint32_t instCount = (uint32_t)cascadeTmd.ObjectIndices.size();
2247+
if (instCount == 0) continue;
21522248

2153-
Ref<SceneRenderer> instance = this;
2154-
Renderer::Submit([instance, drawCmd, cascadeTmd]() mutable {
2155-
instance->RT_DrawStaticMesh(
2156-
instance->m_CommandBuffer, drawCmd, cascadeTmd, /*bindMaterial=*/false);
2157-
});
2158-
}
2249+
StaticDrawCommand drawCmd = dc;
2250+
drawCmd.InstanceCount = instCount;
21592251

2160-
Renderer::EndRenderPass(m_CommandBuffer);
2252+
Ref<SceneRenderer> instance = this;
2253+
Renderer::Submit([instance, drawCmd, cascadeTmd, cascade]() mutable {
2254+
instance->RT_DrawStaticMesh(
2255+
instance->m_CommandBuffer, drawCmd, cascadeTmd, /*bindMaterial=*/false, cascade);
2256+
});
2257+
}
2258+
2259+
Renderer::EndRenderPass(m_CommandBuffer);
2260+
}
21612261
Renderer::EndGPUPerfMarker(m_CommandBuffer);
21622262
}
21632263

Core/Source/Lux/Renderer/SceneRenderer.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "Lux/Scene/Scene.h"
2222

2323
#include <glm/glm.hpp>
24+
#include <array>
2425
#include <limits>
2526
#include <map>
2627
#include <vector>
@@ -107,9 +108,15 @@ namespace Lux {
107108
bool ShowGrid = true;
108109
bool ShowSelectedInWireframe = false;
109110
bool ShowPhysicsColliders = false;
111+
bool ShowShadowCascades = false;
112+
bool ShowLightComplexity = false;
110113
bool SoftShadows = true;
111114
float MaxShadowDistance = 200.0f;
112-
float ShadowFade = 1.0f;
115+
float ShadowFade = 25.0f;
116+
float ShadowCascadeSplitLambda = 0.92f;
117+
float ShadowCascadeNearPlaneOffset = 0.0f;
118+
float ShadowCascadeFarPlaneOffset = 50.0f;
119+
float ShadowCascadeTransitionFade = 1.0f;
113120
bool EnableGTAO = true;
114121
bool GTAOBentNormals = false;
115122
uint32_t GTAODenoisePasses = 4;
@@ -183,6 +190,7 @@ namespace Lux {
183190
class SceneRenderer : public RefCounted
184191
{
185192
public:
193+
static constexpr uint32_t ShadowCascadeCount = 4;
186194
static constexpr uint32_t MaxSpotShadows = 16;
187195
static constexpr uint32_t LightCullingTileSize = 16;
188196
static constexpr uint32_t MaxVisibleLightsPerTile = 256;
@@ -381,6 +389,14 @@ namespace Lux {
381389
void CreateHZBPassMaterials();
382390
void CreatePreIntegrationPassMaterials();
383391
void CreatePreConvolutionPassMaterials();
392+
393+
struct CascadeData
394+
{
395+
glm::mat4 ViewProj{ 1.0f };
396+
float SplitDepth = 0.0f;
397+
};
398+
void CalculateCascades(CascadeData* cascades, const SceneRendererCamera& sceneCamera, const glm::vec3& lightDirection) const;
399+
384400
void BuildIndirectDrawCommand(const StaticDrawCommand& dc,
385401
const TransformMapData& tmd,
386402
std::vector<nvrhi::DrawIndexedIndirectArguments>& drawCommands);
@@ -425,10 +441,9 @@ namespace Lux {
425441
float EnvironmentMapIntensity = 1.0f;
426442
} m_SceneUB;
427443

428-
// Supports up to 4 cascades; we only use [0] (single ortho shadow map).
429444
struct UBShadow
430445
{
431-
glm::mat4 ViewProjection[4];
446+
glm::mat4 ViewProjection[ShadowCascadeCount];
432447
} m_ShadowUB;
433448

434449
struct UBSpotShadow
@@ -549,9 +564,10 @@ namespace Lux {
549564
uint32_t m_LightTilesCountY = 1;
550565
uint32_t m_VisibleLightIndexBufferSize = 0;
551566

552-
// ── Shadow map (single ortho cascade) ────────────────────────────────
567+
// ── Directional shadow maps ─────────────────────────────────────────
553568
Ref<Image2D> m_ShadowMapImage;
554-
Ref<RenderPass> m_ShadowMapPass;
569+
std::array<Ref<RenderPass>, ShadowCascadeCount> m_ShadowMapPasses;
570+
Ref<RenderPass> m_ShadowMapPass; // Alias for cascade 0, used for shared shadow texture binding
555571
Ref<Material> m_ShadowPassMaterial;
556572

557573
// ── Spot shadow atlas ───────────────────────────────────────────────

0 commit comments

Comments
 (0)