VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
renderPassShadowDirectional.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 13.02.2026.
5//
7
8#include <string>
9
15#include "scene/graphNode.h"
17#include "shadowRenderer.h"
19
20namespace visutwin::canvas
21{
23 ShadowRenderer* shadowRenderer, Light* light, Camera* camera, Camera* shadowCamera, const int face, const bool allCascadesRendering)
24 : RenderPass(device), _shadowRenderer(shadowRenderer), _light(light), _camera(camera),
25 _shadowCamera(shadowCamera), _graphicsDevice(device), _face(face), _allCascadesRendering(allCascadesRendering)
26 {
27 _requiresCubemaps = false;
28 _name = "RenderPassShadowDirectional";
29 if (_light && _light->node()) {
30 _name += "-" + _light->node()->name();
31 }
32 _name += "-face" + std::to_string(_face);
33 }
34
36 {
37 if (!_graphicsDevice || !_light) {
38 return;
39 }
40
41 auto programLibrary = getProgramLibrary(_graphicsDevice);
42 if (!programLibrary) {
43 return;
44 }
45
46 auto shadowShader = programLibrary->getShadowShader(false);
47 auto shadowShaderDynBatch = programLibrary->getShadowShader(true);
48 if (!shadowShader) {
49 return;
50 }
51
52 _graphicsDevice->setShader(shadowShader);
53
54 // Shadow pass needs blend/depth state set on the device — the forward pass
55 // sets these per-material, but the shadow pass bypasses materials entirely.
56 static auto shadowBlendState = std::make_shared<BlendState>(); // default: no blend, color writes on
57 static auto shadowDepthState = std::make_shared<DepthState>(); // default: depth test+write enabled
58 _graphicsDevice->setBlendState(shadowBlendState);
59 _graphicsDevice->setDepthState(shadowDepthState);
60
61 // hardware polygon-offset depth bias during shadow rendering.
62 // bias = shadowBias * -1000.0,
63 // applied via device.setDepthState(light.shadowDepthState).
64 // The slope-based bias automatically adds more offset on steep geometry, preventing
65 // acne without requiring excessive fixed bias that would erase self-shadows.
66 {
67 const float bias = _light->shadowBias() * -1000.0f;
68 _graphicsDevice->setDepthBias(bias, bias, 0.0f);
69 }
70
71 // Get shadow map texture dimensions for viewport mapping.
72 const int texSize = _light->shadowResolution();
73
74 // Loop over all cascades, rendering each with its own viewport/scissor.
75 // loops faces.
76 const int faceCount = _light->numShadowFaces();
77 for (int face = 0; face < faceCount; ++face) {
78 LightRenderData* rd = _light->getRenderData(_camera, face);
79 if (!rd || !rd->shadowCamera || !rd->shadowCamera->node()) {
80 continue;
81 }
82
83 Camera* shadowCam = rd->shadowCamera;
84
85 // Map normalized viewport rect to pixel coordinates.
86 const Vector4& vpRect = rd->shadowViewport;
87 const float vpX = vpRect.getX() * static_cast<float>(texSize);
88 const float vpY = vpRect.getY() * static_cast<float>(texSize);
89 const float vpW = vpRect.getZ() * static_cast<float>(texSize);
90 const float vpH = vpRect.getW() * static_cast<float>(texSize);
91
92 _graphicsDevice->setViewport(vpX, vpY, vpW, vpH);
93 _graphicsDevice->setScissor(
94 static_cast<int>(vpX), static_cast<int>(vpY),
95 static_cast<int>(vpW), static_cast<int>(vpH));
96
97 // Compute this cascade's VP matrix from its shadow camera.
98 const Matrix4 viewProjection = shadowCam->projectionMatrix()
99 * shadowCam->node()->worldTransform().inverse();
100
101 for (auto* renderComponent : RenderComponent::instances()) {
102 if (!shouldRenderShadowRenderComponent(renderComponent, _camera)) {
103 continue;
104 }
105
106 for (auto* meshInstance : renderComponent->meshInstances()) {
107 if (!meshInstance->visible()) {
108 continue;
109 }
110 if (!shouldRenderShadowMeshInstance(meshInstance, shadowCam)) {
111 continue;
112 }
113
114 auto vertexBuffer = meshInstance->mesh()->getVertexBuffer();
115 meshInstance->setVisibleThisFrame(true);
116 _graphicsDevice->setVertexBuffer(vertexBuffer, 0);
117
118 if (meshInstance->isDynamicBatch()) {
119 // Dynamic batch: use dynamic batch shadow shader + palette.
120 if (shadowShaderDynBatch) {
121 _graphicsDevice->setShader(shadowShaderDynBatch);
122 }
123 auto* sbi = meshInstance->skinBatchInstance();
124 if (sbi) {
125 _graphicsDevice->setDynamicBatchPalette(sbi->paletteData(), sbi->paletteSizeBytes());
126 }
127 _graphicsDevice->setTransformUniforms(viewProjection, Matrix4::identity());
128 _graphicsDevice->draw(meshInstance->mesh()->getPrimitive(), meshInstance->mesh()->getIndexBuffer(), 1, -1, true, true);
129 // Restore standard shadow shader for next non-dynamic-batch mesh.
130 _graphicsDevice->setShader(shadowShader);
131 } else {
132 const auto modelMatrix = (meshInstance->node() ? meshInstance->node()->worldTransform() : Matrix4::identity());
133 _graphicsDevice->setTransformUniforms(viewProjection, modelMatrix);
134 _graphicsDevice->draw(meshInstance->mesh()->getPrimitive(), meshInstance->mesh()->getIndexBuffer(), 1, -1, true, true);
135 }
136 }
137 }
138 }
139 }
140
142 {
143 // DEVIATION: VSM post-filtering path is not ported yet.
144 (void)_face;
145 }
146}
Perspective or orthographic camera with projection matrix, jitter (TAA), and render target binding.
Definition camera.h:40
const Matrix4 & projectionMatrix()
Definition camera.h:65
const std::unique_ptr< GraphNode > & node() const
Definition camera.h:102
Directional, point, spot, or area light with shadow mapping and cookie projection.
Definition light.h:54
Per-face shadow rendering data: shadow camera, viewport, and scissor.
Definition light.h:25
static const std::vector< RenderComponent * > & instances()
std::shared_ptr< GraphicsDevice > device() const
Definition renderPass.h:124
RenderPass(const std::shared_ptr< GraphicsDevice > &device)
Definition renderPass.h:66
RenderPassShadowDirectional(const std::shared_ptr< GraphicsDevice > &device, ShadowRenderer *shadowRenderer, Light *light, Camera *camera, Camera *shadowCamera, int face, bool allCascadesRendering)
bool shouldRenderShadowMeshInstance(MeshInstance *meshInstance, Camera *shadowCamera)
std::shared_ptr< ProgramLibrary > getProgramLibrary(const std::shared_ptr< GraphicsDevice > &device)
bool shouldRenderShadowRenderComponent(const RenderComponent *renderComponent, const Camera *camera)
4x4 column-major transformation matrix with SIMD acceleration.
Definition matrix4.h:31
static Matrix4 identity()
Definition matrix4.h:108
4D vector for homogeneous coordinates, color values, and SIMD operations.
Definition vector4.h:20
float getX() const
Definition vector4.h:85
float getY() const
Definition vector4.h:98