Skip to content

Commit e55f389

Browse files
authored
Merge pull request #86 from Simple-Robotics/fix-camera-frustum-shadow-pass
Fix shadow pass using camera frustum, remove using-decl for `coal::OBB`
2 parents 70bc60b + 8779870 commit e55f389

File tree

10 files changed

+66
-39
lines changed

10 files changed

+66
-39
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- multibody/Visualizer : expose 'device'
13+
- core : make `renderShadowPassFromFrustum()` work properly
14+
15+
### Removed
16+
17+
- core : remove using-decl of `coal::OBB`
18+
1019
## [0.5.0] - 2025-06-13
1120

1221
### Added

bindings/python/src/expose-visualizer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ void exposeVisualizer() {
3434
.def(VisualizerPythonVisitor<Visualizer>{})
3535
.def_readonly("renderer", &Visualizer::renderer)
3636
.def_readwrite("worldSceneBounds", &Visualizer::worldSceneBounds)
37+
.add_property("device",
38+
bp::make_function(&Visualizer::device,
39+
bp::return_internal_reference<>()))
3740
.def(
3841
"takeScreenshot",
3942
+[](Visualizer &viz, const std::string &filename) {

examples/Ur5WithSystems.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,9 +497,12 @@ int main(int argc, char **argv) {
497497
const GpuMat4 viewProj = g_camera.camera.viewProj();
498498
robot_scene.collectOpaqueCastables();
499499
auto &castables = robot_scene.castables();
500-
renderShadowPassFromAABB(command_buffer, shadowPassInfo,
501-
robot_scene.directionalLight, castables,
502-
worldSpaceBounds);
500+
// renderShadowPassFromAABB(command_buffer, shadowPassInfo,
501+
// robot_scene.directionalLight, castables,
502+
// worldSpaceBounds);
503+
renderShadowPassFromFrustum(command_buffer, shadowPassInfo,
504+
robot_scene.directionalLight, castables,
505+
frustumFromCameraViewProj(viewProj));
503506
depthPass.render(command_buffer, viewProj, castables);
504507
switch (g_showDebugViz) {
505508
case FULL_RENDER:

src/candlewick/core/Camera.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ inline float perspectiveProjFar(const Mat4f &proj) {
104104
return std::abs(proj(2, 3) / (proj(2, 2) + 1.f));
105105
}
106106

107-
/// \brief Extract the array of frustum corners, given a camera projection
108-
/// matrix.
109-
inline FrustumCornersType frustumFromCameraProjection(const Mat4f &camProj) {
107+
/// \brief Extract the array of frustum corners, given the camera
108+
/// view-projection matrix.
109+
inline FrustumCornersType frustumFromCameraViewProj(const Mat4f &camProj) {
110110
auto invProj = camProj.inverse();
111111
FrustumCornersType out;
112112
for (Uint8 i = 0; i < 8; i++) {
@@ -124,11 +124,6 @@ inline FrustumCornersType frustumFromCameraProjection(const Mat4f &camProj) {
124124
return out;
125125
}
126126

127-
/// \brief Get the corners of a Camera's view frustum, in world space.
128-
inline FrustumCornersType frustumFromCamera(const Camera &camera) {
129-
return frustumFromCameraProjection(camera.viewProj());
130-
}
131-
132127
inline std::pair<Float3, float>
133128
frustumBoundingSphereCenterRadius(const FrustumCornersType &worldSpaceCorners) {
134129
Float3 frustumCenter = Float3::Zero();
@@ -144,6 +139,13 @@ frustumBoundingSphereCenterRadius(const FrustumCornersType &worldSpaceCorners) {
144139
return {frustumCenter, radius};
145140
}
146141

142+
inline void frustumApplyTransform(FrustumCornersType &corners,
143+
const Mat4f &tr) {
144+
for (auto &x : corners) {
145+
x = (tr * x.homogeneous()).head<3>();
146+
}
147+
}
148+
147149
/// \}
148150

149151
} // namespace candlewick

src/candlewick/core/Collision.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
namespace candlewick {
77

88
using coal::AABB;
9-
using coal::OBB;
109

1110
inline Mat4f toTransformationMatrix(const AABB &aabb) {
1211
Mat4f T = Mat4f::Identity();
@@ -16,7 +15,7 @@ inline Mat4f toTransformationMatrix(const AABB &aabb) {
1615
return T;
1716
}
1817

19-
inline Mat4f toTransformationMatrix(const OBB &obb) {
18+
inline Mat4f toTransformationMatrix(const coal::OBB &obb) {
2019
Mat4f T = Mat4f::Identity();
2120
auto D = obb.extent.asDiagonal();
2221
T.block<3, 3>(0, 0) = (obb.axes * D).cast<float>();
@@ -40,4 +39,12 @@ inline std::array<Float3, 8> getAABBCorners(const AABB &aabb) {
4039
};
4140
}
4241

42+
inline AABB applyTransformToAABB(const AABB &aabb, const Mat4f &tr_) {
43+
using coal::CoalScalar;
44+
auto tr = tr_.cast<CoalScalar>();
45+
coal::Matrix3s R = tr.topLeftCorner<3, 3>();
46+
coal::Vec3s t = tr.topRightCorner<3, 1>();
47+
return coal::translate(coal::rotate(aabb, R), t);
48+
}
49+
4350
} // namespace candlewick

src/candlewick/core/DepthAndShadowPass.cpp

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -262,21 +262,29 @@ void renderShadowPassFromFrustum(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
262262
std::span<const DirectionalLight> dirLight,
263263
std::span<const OpaqueCastable> castables,
264264
const FrustumCornersType &worldSpaceCorners) {
265-
using Eigen::Vector3d;
266-
267265
auto [center, radius] = frustumBoundingSphereCenterRadius(worldSpaceCorners);
268-
radius = std::ceil(radius * 16.f) / 16.f;
269266

270267
for (size_t i = 0; i < passInfo.numLights(); i++) {
271-
const Float3 eye = center - radius * dirLight[i].direction.normalized();
268+
Float3 eye = center - radius * dirLight[i].direction.normalized();
269+
Mat4f tmpLightView = lookAt(eye, center, Float3::UnitZ());
270+
// transform corners to light-space
271+
FrustumCornersType corners = worldSpaceCorners;
272+
frustumApplyTransform(corners, tmpLightView);
273+
274+
coal::AABB bounds;
275+
for (const auto &c : corners) {
276+
bounds += c.cast<coal::CoalScalar>();
277+
}
278+
float r = float(bounds.max_.z());
279+
272280
auto &lightView = passInfo.cam[i].view;
273281
auto &lightProj = passInfo.cam[i].projection;
282+
eye = center - r * dirLight[i].direction.normalized();
274283
lightView = lookAt(eye, center, Float3::UnitZ());
275284

276-
AABB bounds{Vector3d::Constant(-radius), Vector3d::Constant(radius)};
277285
lightProj = shadowOrthographicMatrix({bounds.width(), bounds.height()},
278-
float(bounds.min_.z()),
279-
float(bounds.max_.z()));
286+
float(bounds.max_.z()),
287+
float(bounds.min_.z()));
280288
}
281289
passInfo.render(cmdBuf, castables);
282290
}
@@ -285,28 +293,21 @@ void renderShadowPassFromAABB(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
285293
std::span<const DirectionalLight> dirLight,
286294
std::span<const OpaqueCastable> castables,
287295
const AABB &worldAABB) {
288-
using Eigen::Vector3d;
289-
290296
Float3 center = worldAABB.center().cast<float>();
291297

292298
for (size_t i = 0; i < passInfo.numLights(); i++) {
293-
Float3 tmpEye = center - 100.0f * dirLight[i].direction.normalized();
294-
Mat4f tmplightView = lookAt(tmpEye, center, Float3::UnitZ());
299+
Float3 eye = center - 100.0f * dirLight[i].direction.normalized();
300+
Mat4f tmpLightView = lookAt(eye, center, Float3::UnitZ());
301+
AABB bounds = applyTransformToAABB(worldAABB, tmpLightView);
295302

296303
auto &lightView = passInfo.cam[i].view;
297304
auto &lightProj = passInfo.cam[i].projection;
298305

299-
Mat3f R = tmplightView.topLeftCorner<3, 3>();
300-
Float3 t = tmplightView.topRightCorner<3, 1>();
301-
AABB bounds = coal::translate(coal::rotate(worldAABB, R.cast<double>()),
302-
t.cast<double>());
303-
304-
Float3 lightSpaceCenter = bounds.center().cast<float>();
305306
float radius = float(bounds.max_.z());
306307

307-
Float3 finalEye = center - radius * dirLight[i].direction.normalized();
308+
eye = center - radius * dirLight[i].direction.normalized();
308309

309-
lightView = lookAt(finalEye, center, Float3::UnitZ());
310+
lightView = lookAt(eye, center, Float3::UnitZ());
310311
lightProj = shadowOrthographicMatrix({bounds.width(), bounds.height()},
311312
float(bounds.max_.z()),
312313
float(bounds.min_.z()));

src/candlewick/core/DepthAndShadowPass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ void renderShadowPassFromAABB(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
190190
/// light-space view and projection matrices which will enclose this bounding
191191
/// sphere within the light volume. The frustum can be obtained from the
192192
/// world-space camera.
193-
/// \sa frustumFromCameraProjection()
193+
/// \sa frustumFromCameraViewProj()
194194
void renderShadowPassFromFrustum(CommandBuffer &cmdBuf, ShadowMapPass &passInfo,
195195
std::span<const DirectionalLight> dirLight,
196196
std::span<const OpaqueCastable> castables,

src/candlewick/core/debug/Frustum.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ namespace frustum_debug {
8585
}
8686

8787
void renderOBB(CommandBuffer &cmdBuf, SDL_GPURenderPass *render_pass,
88-
const Camera &camera, const OBB &obb, const Float4 &color) {
88+
const Camera &camera, const coal::OBB &obb,
89+
const Float4 &color) {
8990
Mat4f transform = toTransformationMatrix(obb);
9091
Mat4f mvp = camera.viewProj() * transform;
9192
Float3 eyePos = obb.center().cast<float>();
@@ -130,7 +131,7 @@ void FrustumBoundsDebugSystem::render(CommandBuffer &cmdBuf,
130131
frustum_debug::renderAABB(cmdBuf, render_pass, camera,
131132
bounds, item.color);
132133
},
133-
[&](const OBB &obb) {
134+
[&](const coal::OBB &obb) {
134135
frustum_debug::renderOBB(cmdBuf, render_pass, camera, obb,
135136
item.color);
136137
},

src/candlewick/core/debug/Frustum.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace frustum_debug {
2424
const Float4 &color = 0x00BFFFff_rgbaf);
2525

2626
void renderOBB(CommandBuffer &cmdBuf, SDL_GPURenderPass *render_pass,
27-
const Camera &camera, const OBB &obb,
27+
const Camera &camera, const coal::OBB &obb,
2828
const Float4 &color = 0x00BFFFff_rgbaf);
2929

3030
void renderFrustum(CommandBuffer &cmdBuf, SDL_GPURenderPass *render_pass,
@@ -39,7 +39,7 @@ struct DebugFrustumComponent {
3939
};
4040

4141
struct DebugBoundsComponent {
42-
std::variant<AABB, OBB> bounds;
42+
std::variant<AABB, coal::OBB> bounds;
4343
GpuVec4 color = 0xA03232FF_rgbaf;
4444
};
4545

src/candlewick/multibody/Visualizer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ class Visualizer final : public BaseVisualizer {
6969

7070
void displayImpl() override;
7171

72-
const Device &device() const { return renderer.device; }
73-
7472
GuiSystem::GuiBehavior getDefaultCallback() {
7573
return [this](auto &) { this->defaultGuiCallback(); };
7674
}
@@ -105,6 +103,7 @@ class Visualizer final : public BaseVisualizer {
105103
void defaultGuiCallback();
106104

107105
void resetCamera();
106+
108107
void loadViewerModel() override;
109108

110109
Visualizer(const Config &config, const pin::Model &model,
@@ -127,6 +126,8 @@ class Visualizer final : public BaseVisualizer {
127126

128127
~Visualizer() override;
129128

129+
const Device &device() const { return renderer.device; }
130+
130131
void setCameraTarget(const Eigen::Ref<const Vector3> &target) override;
131132

132133
void setCameraPosition(const Eigen::Ref<const Vector3> &position) override;

0 commit comments

Comments
 (0)