Skip to content

Commit 83e5d43

Browse files
Vinicius [C] GuimaraesVinicius Guimaraes
authored andcommitted
Add SSAO support to the VTK renderer
1 parent fec2751 commit 83e5d43

File tree

6 files changed

+156
-1
lines changed

6 files changed

+156
-1
lines changed

bindings/pydrake/geometry/geometry_py_render.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ using geometry::render::DepthRenderCamera;
2727
using geometry::render::LightParameter;
2828
using geometry::render::LightType;
2929
using geometry::render::RenderEngine;
30+
using geometry::render::SsaoParameter;
3031
using math::RigidTransformd;
3132
using systems::sensors::CameraInfo;
3233
using systems::sensors::Image;
@@ -414,6 +415,17 @@ void DoScalarIndependentDefinitions(py::module m) {
414415
DefCopyAndDeepCopy(&cls);
415416
}
416417

418+
{
419+
using Class = geometry::render::SsaoParameter;
420+
constexpr auto& cls_doc = doc.SsaoParameter;
421+
py::class_<Class> cls(m, "SsaoParameter", cls_doc.doc);
422+
cls // BR
423+
.def(ParamInit<Class>());
424+
DefAttributesUsingSerialize(&cls);
425+
DefReprUsingSerialize(&cls);
426+
DefCopyAndDeepCopy(&cls);
427+
}
428+
417429
{
418430
using Class = RenderEngineVtkParams;
419431
constexpr auto& cls_doc = doc_geometry.RenderEngineVtkParams;

geometry/render_vtk/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ drake_cc_googletest(
150150
timeout = "moderate",
151151
data = [
152152
":test/multi_material_mesh.png",
153+
":test/whole_image_custom_and_SSAO_color.png",
153154
":test/whole_image_custom_color.png",
154155
":test/whole_image_custom_depth.png",
155156
":test/whole_image_custom_label.png",

geometry/render_vtk/internal_render_engine_vtk.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <vtkPointData.h> // vtkCommonDataModel
3131
#include <vtkProperty.h> // vtkRenderingCore
3232
#include <vtkRenderPassCollection.h> // vtkRenderingOpenGL2
33+
#include <vtkSSAOPass.h> // vtkRenderingOpenGL2
3334
#include <vtkSequencePass.h> // vtkRenderingOpenGL2
3435
#include <vtkShadowMapBakerPass.h> // vtkRenderingOpenGL2
3536
#include <vtkShadowMapPass.h> // vtkRenderingOpenGL2
@@ -962,7 +963,27 @@ void RenderEngineVtk::InitializePipelines() {
962963
vtkNew<vtkSequencePass> full_seq;
963964
vtkNew<vtkRenderPassCollection> full_passes;
964965
full_passes->AddItem(vtkNew<vtkLightsPass>());
965-
full_passes->AddItem(vtkNew<vtkOpaquePass>());
966+
if (parameters_.ssao_params.has_value()) {
967+
vtkNew<vtkCameraPass> ssao_camera_pass;
968+
vtkNew<vtkOpaquePass> opaque_pass;
969+
ssao_camera_pass->SetDelegatePass(opaque_pass);
970+
971+
vtkNew<vtkSSAOPass> ssao_pass;
972+
const auto& ssao_parameter = parameters_.ssao_params.value();
973+
ssao_pass->SetDelegatePass(ssao_camera_pass);
974+
ssao_pass->SetRadius(ssao_parameter.radius); // comparison radius
975+
ssao_pass->SetBias(ssao_parameter.bias); // comparison bias
976+
ssao_pass->SetKernelSize(
977+
ssao_parameter.kernel_size); // number of samples used
978+
ssao_pass->SetIntensityScale(ssao_parameter.intensity_scale);
979+
ssao_pass->SetIntensityShift(ssao_parameter.intensity_shift);
980+
if (!ssao_parameter.blur) {
981+
ssao_pass->BlurOff(); // do not blur occlusion
982+
}
983+
full_passes->AddItem(ssao_pass);
984+
} else {
985+
full_passes->AddItem(vtkNew<vtkOpaquePass>());
986+
}
966987
full_passes->AddItem(vtkNew<vtkTranslucentPass>());
967988
full_seq->SetPasses(full_passes);
968989

geometry/render_vtk/render_engine_vtk_params.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,30 @@
1414
namespace drake {
1515
namespace geometry {
1616

17+
namespace render {
18+
/** SSAO parameter for supporting SSAO feature in VTK rendering. */
19+
struct SsaoParameter {
20+
/** Passes this object to an Archive.
21+
Refer to @ref yaml_serialization "YAML Serialization" for background. */
22+
template <typename Archive>
23+
void Serialize(Archive* a) {
24+
a->Visit(DRAKE_NVP(radius));
25+
a->Visit(DRAKE_NVP(bias));
26+
a->Visit(DRAKE_NVP(kernel_size));
27+
a->Visit(DRAKE_NVP(intensity_scale));
28+
a->Visit(DRAKE_NVP(intensity_shift));
29+
a->Visit(DRAKE_NVP(blur));
30+
}
31+
32+
double radius{0.5};
33+
double bias{0.01};
34+
int kernel_size{32};
35+
double intensity_scale{1.0};
36+
double intensity_shift{0.0};
37+
bool blur{false};
38+
};
39+
} // namespace render
40+
1741
// TODO(SeanCurtis-TRI): When CubeMap is implemented, replace NullTexture with
1842
// CubeMap.
1943

@@ -101,6 +125,7 @@ struct RenderEngineVtkParams {
101125
a->Visit(DRAKE_NVP(lights));
102126
a->Visit(DRAKE_NVP(environment_map));
103127
a->Visit(DRAKE_NVP(exposure));
128+
a->Visit(DRAKE_NVP(ssao_params));
104129
a->Visit(DRAKE_NVP(cast_shadows));
105130
a->Visit(DRAKE_NVP(shadow_map_size));
106131
a->Visit(DRAKE_NVP(force_to_pbr));
@@ -174,6 +199,21 @@ struct RenderEngineVtkParams {
174199
*/
175200
std::optional<double> exposure{};
176201

202+
/** An optional SSAO (screen-space ambient occlusion) parameters set. When
203+
* specified, VTK will use the parameters provided for SSAO and simulate the
204+
* ambient occlusion on surfaces where light is obstructed by the surrounding
205+
* geometry, producing images with better qualities and higher fidelity.
206+
* See https://www.kitware.com/ssao/ for details.
207+
* Non-specified parameters will use default values:
208+
* radius: 0.5
209+
* bias: 0.01
210+
* kernel size: 32
211+
* intensity_scale: 1.0
212+
* intensity_shift: 0.0
213+
* blur: false
214+
*/
215+
std::optional<render::SsaoParameter> ssao_params{};
216+
177217
/** If `true`, *all* lights that are *able* to cast shadows will do so.
178218
179219
Several important notes when designing your lighting:

geometry/render_vtk/test/internal_render_engine_vtk_test.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,87 @@ TEST_F(RenderEngineVtkTest, WholeImageCustomParams) {
30973097
}
30983098
}
30993099

3100+
// Like WholeImageCustomParams, except we introduce the SSAO pass to the render
3101+
// engine parameters to detect differences.
3102+
TEST_F(RenderEngineVtkTest, WholeImageCustomParamsAndSSAO) {
3103+
const double last_x =
3104+
AddShapeRows(nullptr, {.gltf_mesh = true, .no_half_space = true});
3105+
const RigidTransformd X_WR(RotationMatrixd::MakeXRotation(-3.2 * M_PI / 4),
3106+
Vector3d(last_x / 2, -0.75, 1.1));
3107+
3108+
// Use default SSAO values
3109+
const drake::geometry::render::SsaoParameter ssaoParams{};
3110+
vector<LightParameter> lights_W{{.type = "point",
3111+
.color = Rgba(1.0, 0.75, 0.75),
3112+
.attenuation_values = {0, 0, 1},
3113+
.position = Vector3d(0, 0, 0.3),
3114+
.frame = "world",
3115+
.intensity = 0.5},
3116+
{.type = "spot",
3117+
.color = Rgba(0.75, 1.0, 0.75),
3118+
.position = Vector3d(0, -0.1, 2),
3119+
.frame = "world",
3120+
.intensity = 1,
3121+
.direction = Vector3d(-0.5, 0, -1),
3122+
.cone_angle = 10},
3123+
{.type = "directional",
3124+
.color = Rgba(1, 1, 1),
3125+
.frame = "world",
3126+
.intensity = 1,
3127+
.direction = Vector3d(0.02, -0.05, -1)}};
3128+
// We should get the same images whether the lights are expressed in the
3129+
// world frame or the camera frame.
3130+
// TODO(SeanCurtis-TRI): When we can edit the lights after instantiation,
3131+
// simplify this so we don't re-instantiate the engine.
3132+
for (const vector<LightParameter>& lights :
3133+
{lights_W, TransformLightsToCamera(lights_W, X_WR)}) {
3134+
const RenderEngineVtkParams params{
3135+
.default_diffuse = Eigen::Vector4d(0.1, 0.2, 0.4, 1.0),
3136+
.default_clear_color = Vector3d(0.25, 0.25, 0.25),
3137+
.lights = lights,
3138+
.environment_map = {},
3139+
.exposure = 0.75,
3140+
.ssao_params = ssaoParams,
3141+
.cast_shadows = true,
3142+
.shadow_map_size = 1024,
3143+
.backend = FLAGS_backend,
3144+
};
3145+
RenderEngineVtk engine(params);
3146+
3147+
// We'll use the same camera as the default camera for the tests, but shrink
3148+
// the image size so they're not as big in the repository.
3149+
const int w = 480;
3150+
const int h = 360;
3151+
const CameraInfo& source_intrinsics = depth_camera_.core().intrinsics();
3152+
const CameraInfo intrinsics(w, h, source_intrinsics.fov_y());
3153+
const DepthRenderCamera camera(
3154+
{"unused", intrinsics, depth_camera_.core().clipping(),
3155+
depth_camera_.core().sensor_pose_in_camera_body()},
3156+
depth_camera_.depth_range());
3157+
3158+
// The value we used to define the camera pose better match.
3159+
DRAKE_DEMAND(AddShapeRows(&engine, {.gltf_mesh = true,
3160+
.no_half_space = true}) == last_x);
3161+
3162+
// Now make the rendering.
3163+
engine.UpdateViewpoint(X_WR);
3164+
3165+
ImageRgba8U color(w, h);
3166+
ImageDepth32F depth(w, h);
3167+
ImageLabel16I label(w, h);
3168+
this->Render(fmt::format("whole_image_custom_color_{}", lights[0].frame),
3169+
&engine, &camera, &color, &depth, &label);
3170+
3171+
{
3172+
SCOPED_TRACE(fmt::format("Color image - {}", lights[0].frame));
3173+
CompareImages(color,
3174+
"drake/geometry/render_vtk/test/"
3175+
"whole_image_custom_and_SSAO_color.png",
3176+
/* tolerance = */ 25);
3177+
}
3178+
}
3179+
}
3180+
31003181
// To work around a bug in VTK, when shadows are enabled, we have to render into
31013182
// a square window, and then crop the image back out. The WholeImageCustomParams
31023183
// implicitly handles non-square images with a *horizontal* aspect ratio. This
269 KB
Loading

0 commit comments

Comments
 (0)