Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RenderEngineVtk can be forced into PBR mode #22170

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion geometry/render_vtk/internal_render_engine_vtk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ RenderEngineVtk::RenderEngineVtk(const RenderEngineVtkParams& parameters)
for (auto& pipeline : pipelines_) {
pipeline = make_unique<RenderingPipeline>(backend);
}

// If it has been explicitly requested, we'll start with PBR. Otherwise, we'll
// defer to the rules for promotion to PBR.
use_pbr_materials_ = parameters.force_to_pbr;

// Only populate the fallback lights if we haven't specified an environment
// map.
// Until we introduce CubeMap, the default texture (NullTexture) should be
Expand Down Expand Up @@ -515,7 +520,8 @@ RenderEngineVtk::RenderEngineVtk(const RenderEngineVtk& other)
make_unique<RenderingPipeline>(other.pipelines_[2]->backend)}},
default_diffuse_{other.default_diffuse_},
default_clear_color_{other.default_clear_color_},
fallback_lights_(other.fallback_lights_) {
fallback_lights_(other.fallback_lights_),
use_pbr_materials_(other.use_pbr_materials_) {
InitializePipelines();

for (const auto& [id, source_props] : other.props_) {
Expand Down
15 changes: 15 additions & 0 deletions geometry/render_vtk/render_engine_vtk_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ struct RenderEngineVtkParams {
a->Visit(DRAKE_NVP(exposure));
a->Visit(DRAKE_NVP(cast_shadows));
a->Visit(DRAKE_NVP(shadow_map_size));
a->Visit(DRAKE_NVP(force_to_pbr));
a->Visit(DRAKE_NVP(gltf_extensions));
a->Visit(DRAKE_NVP(backend));
}
Expand Down Expand Up @@ -212,6 +213,20 @@ struct RenderEngineVtkParams {
shadow maps. */
int shadow_map_size{256};

/** RenderEngineVtk can use one of two illumination models: Phong or
Physically based rendering (PBR). It defaults to Phong. However, it
automatically switches to PBR if:

- an environment map is added, or
- a glTF mesh is added.

If `force_to_pbr` is set to true, it switches the engine to use PBR
regardless of the scene's contents.

Be aware, switching to PBR will lead to a qualitative change in rendered
images even if literally nothing else changes. */
bool force_to_pbr{false};

/** Map from the name of a glTF extension (e.g., "KHR_materials_sheen") to
render engine settings related to that extension. */
string_map<GltfExtension> gltf_extensions{
Expand Down
93 changes: 88 additions & 5 deletions geometry/render_vtk/test/internal_render_engine_vtk_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1956,17 +1956,19 @@ TEST_F(RenderEngineVtkTest, EnvironmentMap) {
}
}

// RenderEngineVtk promotes all materials to be PBR materials on two conditions:
// RenderEngineVtk promotes all materials to be PBR materials on either of
// several conditions:
//
// 1. Any geometry with intrinsic PBR materials is introduced (e.g., a glTF)
// 2. An environment map is introduced.
// 3. RenderEngineVtkParams::force_to_pbr is set to `true`.
//
// (1) has been shown in TEST_F(RenderEngineVtkTest, EnvironmentMap). The
// (1) is confirmed by this test.
// (2) Is confirmed by TEST_F(RenderEngineVtkTest, EnvironmentMap). The
// sphere there has a typical phong material and the fact that it gets
// illuminated based on the environment shows PBR promotion.
//
// This test we'll simply confirm that the introduction of a glTF shows an
// illumination change without any other step (indicating material promotion).
// (3) Is confirmed in TEST_F(RenderEngineVtkTest,
// PbrMaterialSettingSurvivesCloning).
TEST_F(RenderEngineVtkTest, PbrMaterialPromotion) {
auto test_sphere_color = [this](int caller_line_number,
const TestColor expected_color,
Expand Down Expand Up @@ -2030,6 +2032,87 @@ TEST_F(RenderEngineVtkTest, PbrMaterialPromotion) {
}
}

// Materials get promoted from Phong to PBR based on various conditions. When
// a source engine gets promoted and then cloned, the clone should retain that
// logic that subsequent geometries added to the clone should also use a PBR
// material.
// This test also serves as the test for RenderEngineVtkParams::force_to_pbr as
// a promotion criterion.
TEST_F(RenderEngineVtkTest, PbrMaterialSettingSurvivesCloning) {
// First test the efficacy of `force_to_pbr`.
// Two otherwise identical engines with the same geometries should have images
// that don't match if one is PBR and the other is phong.
RenderEngineVtk phong_renderer(RenderEngineVtkParams{});
RenderEngineVtk pbr_renderer(RenderEngineVtkParams{.force_to_pbr = true});
// TODO(20002) There is known conflict when we alternate renderings between
// a RenderEngine and its clone. So, we're setting up this "back up" render
// engine that should be identical to pbr_renderer. When the issue is
// resolved, we'll be able to use pbr_renderer in place of this one to
// create the reference images.
RenderEngineVtk renderer_20002(RenderEngineVtkParams{.force_to_pbr = true});

// Pose the camera so we can see three sides of the cubes.
const RotationMatrixd R_WR(math::RollPitchYawd(-0.75 * M_PI, 0, M_PI_4));
const RigidTransformd X_WR(R_WR,
R_WR * -Vector3d(0, 0, 1.5 * kDefaultDistance));

InitializeRenderer(X_WR, /* add_terrain = */ true, &phong_renderer);
InitializeRenderer(X_WR, /* add_terrain = */ true, &pbr_renderer);
InitializeRenderer(X_WR, /* add_terrain = */ true, &renderer_20002);

const fs::path obj_path =
FindResourceOrThrow("drake/geometry/render/test/meshes/rainbow_box.obj");
const Mesh cube(obj_path);
PerceptionProperties props;
props.AddProperty("label", "id", RenderLabel(17));
const GeometryId id = GeometryId::get_new_id();

phong_renderer.RegisterVisual(id, cube, props, RigidTransformd(), false);
pbr_renderer.RegisterVisual(id, cube, props, RigidTransformd(), false);
renderer_20002.RegisterVisual(id, cube, props, RigidTransformd(), false);

ImageRgba8U reference_image(kWidth, kHeight);
Render(__LINE__, "phong", &phong_renderer, nullptr, &reference_image, nullptr,
nullptr);
ImageRgba8U pbr_image(kWidth, kHeight);
Render(__LINE__, "pbr", &pbr_renderer, nullptr, &pbr_image, nullptr, nullptr);

// The images don't match because the `force_to_pbr` changed the light model.
EXPECT_NE(reference_image, pbr_image);

// Make sure the back up renderer matches.
Render(0, "", &renderer_20002, nullptr, &reference_image, nullptr, nullptr);
EXPECT_EQ(reference_image, pbr_image);

// Test post-cloning logic.
unique_ptr<RenderEngine> clone_base = pbr_renderer.Clone();
RenderEngineVtk* clone = static_cast<RenderEngineVtk*>(clone_base.get());
ImageRgba8U clone_image(kWidth, kHeight);
// TODO(20002): The known issue of clone vs source render engine fighting
// can be *mitigated* by performing a throw-away rendering. So, we'll do an
// extra rendering and then do one for real.
Render(0, "", clone, nullptr, &clone_image, nullptr, nullptr);
Render(__LINE__, "clone", clone, nullptr, &clone_image, nullptr, nullptr);

// The cloned engine still renders images with PBR materials.
EXPECT_EQ(clone_image, pbr_image);

// Now, we'll add a new geometry and confirm that the new geometry gets
// promoted to PBR in both images.
const GeometryId ball_id = GeometryId::get_new_id();
const Sphere sphere(0.5);
const RigidTransformd X_WS(Vector3d(0, 0, 1));
props.AddProperty("phong", "diffuse", Rgba(1, 1, 1));
renderer_20002.RegisterVisual(ball_id, sphere, props, X_WS, false);
clone->RegisterVisual(ball_id, sphere, props, X_WS, false);

Render(__LINE__, "pbr with ball", &renderer_20002, nullptr, &pbr_image,
nullptr, nullptr);
Render(__LINE__, "clone with ball", clone, nullptr, &clone_image, nullptr,
nullptr);
EXPECT_EQ(clone_image, pbr_image);
}

namespace {

// Defines the relationship between two adjacent pixels in a rendering of a box.
Expand Down