Use separate SceneView for main scene and UX.

Refactor FilamentContext to allow multiple render requests to
be submitted onto a single RenderTarget. The main scene and the
ux scene are then submitted as two separate requests that share
the same target, resulting in a single, composited render.

PiperOrigin-RevId: 904324801
Change-Id: I7c47ec4f8d159320612fa33384c01e8db3414daa
This commit is contained in:
Haroon Qureshi
2026-04-23 02:22:53 -07:00
committed by Copybara-Service
parent c41ed42407
commit 57ad7463b1
11 changed files with 244 additions and 195 deletions
@@ -36,6 +36,7 @@
#include <mujoco/mujoco.h>
#include "experimental/filament/filament/filament_platform_factory.h"
#include "experimental/filament/filament/object_manager.h"
#include "experimental/filament/filament/render_target.h"
#include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/render_context_filament.h"
@@ -81,77 +82,91 @@ FilamentContext::~FilamentContext() {
FilamentContext::FrameHandle FilamentContext::Render(
std::span<const RenderRequest> requests,
std::span<const ReadPixelsRequest> read_requests) {
if (requests.size() != 1) {
mju_error("Only one render request is supported for now.");
}
if (read_requests.size() > 1) {
mju_error("Only one read request is supported for now.");
}
const RenderRequest& request = requests[0];
if (request.target == nullptr) {
if (!read_requests.empty()) {
mju_error("Cannot read pixels from the window.");
}
if constexpr (UTILS_HAS_THREADING) {
// Wait until previous frame is completed before requesting a new frame.
engine_->flushAndWait();
}
// If the window size has changed, we need to reacquire the swap chain.
if (request.width != window_width_ || request.height != window_height_) {
if (window_width_ != 0 && window_height_ != 0) {
engine_->destroy(window_swap_chain_);
window_swap_chain_ = engine_->createSwapChain(config_.native_window);
}
window_width_ = request.width;
window_height_ = request.height;
}
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.enable_ux = request.draw_ux;
if (renderer_->beginFrame(window_swap_chain_)) {
request.scene->Render(renderer_, scene_view_request);
bool render_began = false;
RenderTarget* current_target = nullptr;
for (const RenderRequest& request : requests) {
if (request.target != current_target && render_began) {
renderer_->endFrame();
render_began = false;
}
current_target = request.target;
if (current_target == nullptr) {
if (!read_requests.empty()) {
mju_error("Cannot read pixels from the window.");
}
if constexpr (UTILS_HAS_THREADING) {
// Wait until previous frame is completed before requesting a new frame.
engine_->flushAndWait();
}
// If the window size has changed, we need to reacquire the swap chain.
if (request.width != window_width_ || request.height != window_height_) {
if (window_width_ != 0 && window_height_ != 0) {
engine_->destroy(window_swap_chain_);
window_swap_chain_ = engine_->createSwapChain(config_.native_window);
}
window_width_ = request.width;
window_height_ = request.height;
}
if (!render_began) {
render_began = renderer_->beginFrame(window_swap_chain_);
}
if (render_began) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
request.scene->Render(renderer_, scene_view_request);
}
} else {
if (read_requests.empty()) {
mju_error(
"Rendering to a render target without a read request is pointless.");
}
const ReadPixelsRequest& read_request = read_requests[0];
if (read_request.num_bytes == 0) {
mju_error("Output buffer size is zero.");
}
if (!render_began) {
render_began = renderer_->beginFrame(offscreen_swap_chain_);
}
if (render_began) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.target = request.target;
request.scene->Render(renderer_, scene_view_request);
request.target->ReadColorPixels(renderer_, read_request.output,
read_request.num_bytes);
}
}
}
if (render_began) {
renderer_->endFrame();
render_began = false;
if constexpr (!UTILS_HAS_THREADING) {
engine_->execute();
}
} else {
if (read_requests.empty()) {
mju_error(
"Rendering to a render target without a read request is pointless.");
}
}
const ReadPixelsRequest& read_request = read_requests[0];
if (read_request.num_bytes == 0) {
mju_error("Output buffer size is zero.");
}
if (renderer_->beginFrame(offscreen_swap_chain_)) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.enable_ux = request.draw_ux;
scene_view_request.target = request.target;
request.scene->Render(renderer_, scene_view_request);
request.target->ReadColorPixels(renderer_, read_request.output,
read_request.num_bytes);
renderer_->endFrame();
}
if (!read_requests.empty()) {
engine_->flushAndWait();
if (read_request.read_completed_callback) {
read_request.read_completed_callback(read_request.user_data);
if (read_requests[0].read_completed_callback) {
read_requests[0].read_completed_callback(read_requests[0].user_data);
}
}
return ++frame_counter_;
}
@@ -59,10 +59,6 @@ class FilamentContext {
// will be rendered to the window (as previously configured in
// mjrFilamentConfig::native_window).
RenderTarget* target = nullptr;
// Whether or not to include the UX scene in the render. (The SceneView
// stores both a main simulation scene and the UX scene.)
bool draw_ux = true;
};
// Information needed to read pixels from a render target.
@@ -37,8 +37,13 @@ namespace mujoco {
using filament::math::float3;
using filament::math::mat3f;
ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view)
: object_mgr_(object_mgr), scene_view_(scene_view) {}
ImguiBridge::ImguiBridge(ObjectManager* object_mgr)
: object_mgr_(object_mgr) {
scene_view_ = std::make_unique<SceneView>(object_mgr_->GetEngine());
scene_view_->DisableShadows();
scene_view_->DisableReflections();
scene_view_->DisablePostProcessing();
}
ImguiBridge::~ImguiBridge() { PrepareRenderables(0); }
@@ -279,10 +284,10 @@ void ImguiBridge::PrepareRenderables(int count) {
r->SetCastShadows(false);
r->SetReceiveShadows(false);
r->SetBlendOrder(static_cast<std::uint16_t>(renderables_.size()));
scene_view_->AddToUxScene(r.get());
scene_view_->AddToScene(r.get());
}
while (renderables_.size() > count) {
scene_view_->RemoveFromUxScene(renderables_.back().get());
scene_view_->RemoveFromScene(renderables_.back().get());
renderables_.pop_back();
}
}
@@ -29,10 +29,10 @@
namespace mujoco {
// Manages Renderables that will be added a SceneView's UX scene.
// Creates and manages a SceneView using data read from ImGui.
class ImguiBridge {
public:
ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view);
explicit ImguiBridge(ObjectManager* object_mgr);
~ImguiBridge();
// Prepares the Renderables using data from the current ImGui state. This
@@ -40,6 +40,9 @@ class ImguiBridge {
// synced.
void Update();
// Returns the managed UX scene.
SceneView* GetSceneView() const { return scene_view_.get(); }
// Uploads texture to be used with ImGui's Image and ImageButton functions.
uintptr_t UploadImage(uintptr_t tex_id, const uint8_t* pixels, int width,
int height, int bpp);
@@ -57,7 +60,7 @@ class ImguiBridge {
void DestroyTexture(ImTextureData* data);
ObjectManager* object_mgr_ = nullptr;
SceneView* scene_view_ = nullptr;
std::unique_ptr<SceneView> scene_view_;
std::vector<std::unique_ptr<Renderable>> renderables_;
std::vector<std::unique_ptr<Mesh>> meshes_;
std::unordered_map<uintptr_t, std::unique_ptr<Texture>> textures_;
@@ -14,6 +14,7 @@
#include "experimental/filament/filament/mjr_filament_renderer.h"
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -28,7 +29,6 @@
#include "experimental/filament/filament/model_util.h"
#include "experimental/filament/filament/render_target.h"
#include "experimental/filament/filament/scene_bridge.h"
#include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/filament/texture.h"
#include "experimental/filament/render_context_filament.h"
@@ -39,11 +39,31 @@ MjrFilamentRenderer::MjrFilamentRenderer(const mjrFilamentConfig* config)
}
void MjrFilamentRenderer::Init(const mjModel* model) {
scene_view_ = std::make_unique<SceneView>(GetEngine());
scene_bridge_ = std::make_unique<SceneBridge>(GetObjectManager(),
scene_view_.get(), model);
imgui_bridge_ =
std::make_unique<ImguiBridge>(GetObjectManager(), scene_view_.get());
scene_bridge_ = std::make_unique<SceneBridge>(GetObjectManager(), model);
imgui_bridge_ = std::make_unique<ImguiBridge>(GetObjectManager());
render_requests_[0].scene = scene_bridge_->GetSceneView();
render_requests_[0].draw_mode = DrawMode::Color;
render_requests_[1].scene = imgui_bridge_->GetSceneView();
render_requests_[1].draw_mode = DrawMode::Color;
// The UX camera is a fixed orthographic camera. We only need to change the
// width/height based on the viewport per frame.
render_requests_[1].camera.orthographic = true;
render_requests_[1].camera.pos[0] = 0.0f;
render_requests_[1].camera.pos[1] = 0.0f;
render_requests_[1].camera.pos[2] = 1.0f;
render_requests_[1].camera.forward[0] = 0.0f;
render_requests_[1].camera.forward[1] = 0.0f;
render_requests_[1].camera.forward[2] = -1.0f;
render_requests_[1].camera.up[0] = 0.0f;
render_requests_[1].camera.up[1] = 1.0f;
render_requests_[1].camera.up[2] = 0.0f;
render_requests_[1].camera.frustum_top = 0.0f;
render_requests_[1].camera.frustum_near = 0.0f;
render_requests_[1].camera.frustum_far = 1.0f;
SetClearColor(ReadElement(model, "filament.clearColor",
filament::math::float4(0, 0, 0, 1)));
@@ -53,46 +73,45 @@ void MjrFilamentRenderer::Render(const mjrRect& viewport, const mjvScene* scene)
scene_bridge_->Update(viewport, scene);
// Update the UX renderable entity after processing the scene in case there
// are any elements in the scene which generate UX draw calls (e.g. labels).
if (imgui_bridge_ && gui_swap_chain_target_ == scene_swap_chain_target_) {
// Prepare the filament Renderable that contains the GUI draw commands. We
// must call this function even if we do not plan on rendering the GUI to
// ensure the ImGui state is updated.
if (mode_ != FrameBufferMode::OffScreen) {
imgui_bridge_->Update();
}
last_render_mode_ = DrawMode::Color;
if (scene->flags[mjRND_SEGMENT]) {
last_render_mode_ = DrawMode::Segmentation;
render_requests_[0].draw_mode = DrawMode::Segmentation;
} else if (scene->flags[mjRND_DEPTH]) {
last_render_mode_ = DrawMode::Depth;
render_requests_[0].draw_mode = DrawMode::Depth;
} else {
render_requests_[0].draw_mode = DrawMode::Color;
}
last_camera_ = mjv_averageCamera(scene->camera, scene->camera + 1);
if (scene_swap_chain_target_ == kWindowSwapChain) {
RenderRequest request;
request.scene = scene_view_.get();
request.draw_mode = last_render_mode_;
request.camera = last_camera_;
request.draw_ux = (gui_swap_chain_target_ == kWindowSwapChain);
request.width = viewport.width;
request.height = viewport.height;
FilamentContext::Render({&request, 1});
render_requests_[0].width = viewport.width;
render_requests_[0].height = viewport.height;
render_requests_[1].width = viewport.width;
render_requests_[1].height = viewport.height;
render_requests_[0].camera = mjv_averageCamera(scene->camera, scene->camera + 1);
render_requests_[1].camera.frustum_center = viewport.width / 2.0f;
render_requests_[1].camera.frustum_width = viewport.width / 2.0f;
render_requests_[1].camera.frustum_bottom = viewport.height;
if (mode_ == FrameBufferMode::Window) {
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
FilamentContext::Render(render_requests_);
}
}
void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) {
switch (framebuffer) {
case mjFB_WINDOW:
scene_swap_chain_target_ = kWindowSwapChain;
gui_swap_chain_target_ = kWindowSwapChain;
mode_ = FrameBufferMode::Window;
break;
case mjFB_OFFSCREEN:
scene_swap_chain_target_ = kOffscreenSwapChain;
gui_swap_chain_target_ = kWindowSwapChain;
mode_ = FrameBufferMode::OffScreen;
break;
case 2: // No official constant fo this.
scene_swap_chain_target_ = kOffscreenSwapChain;
gui_swap_chain_target_ = kOffscreenSwapChain;
mode_ = FrameBufferMode::OffScreenWithGui;
break;
default:
mju_error("Invalid framebuffer mode: %d", framebuffer);
@@ -101,53 +120,62 @@ void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) {
void MjrFilamentRenderer::ReadPixels(mjrRect viewport, unsigned char* rgb,
float* depth) {
if (scene_swap_chain_target_ != kOffscreenSwapChain) {
if (mode_ == FrameBufferMode::Window) {
mju_error("ReadPixels is only supported for offscreen rendering.");
}
RenderRequest request;
request.scene = scene_view_.get();
request.camera = last_camera_;
request.draw_ux = (gui_swap_chain_target_ == kOffscreenSwapChain);
request.width = viewport.width;
request.height = viewport.height;
render_requests_[0].width = viewport.width;
render_requests_[0].height = viewport.height;
render_requests_[1].width = viewport.width;
render_requests_[1].height = viewport.height;
if (rgb) {
request.draw_mode = last_render_mode_;
RenderTargetConfig config;
DefaultRenderTargetConfig(&config);
config.color_format = mjPIXEL_FORMAT_RGB8;
config.depth_format = mjPIXEL_FORMAT_DEPTH32F;
auto target = std::make_unique<RenderTarget>(GetEngine(), config);
target->Prepare(request.width, request.height);
request.target = target.get();
target->Prepare(viewport.width, viewport.height);
render_requests_[0].target = target.get();
render_requests_[1].target = target.get();
const size_t num_requests =
(mode_ == FrameBufferMode::OffScreenWithGui) ? 2 : 1;
ReadPixelsRequest read_request;
read_request.output = rgb;
read_request.num_bytes = viewport.width * viewport.height * 3;
const FrameHandle frame =
FilamentContext::Render({&request, 1}, {&read_request, 1});
const FrameHandle frame = FilamentContext::Render(
{&render_requests_[0], num_requests}, {&read_request, 1});
FilamentContext::WaitForFrame(frame);
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
}
if (depth) {
request.draw_mode = DrawMode::Depth;
RenderTargetConfig config;
DefaultRenderTargetConfig(&config);
config.color_format = mjPIXEL_FORMAT_R32F;
config.depth_format = mjPIXEL_FORMAT_DEPTH32F;
auto target = std::make_unique<RenderTarget>(GetEngine(), config);
target->Prepare(request.width, request.height);
request.target = target.get();
target->Prepare(viewport.width, viewport.height);
render_requests_[0].target = target.get();
render_requests_[1].target = target.get();
DrawMode last_draw_mode = render_requests_[0].draw_mode;
render_requests_[0].draw_mode = DrawMode::Depth;
ReadPixelsRequest read_request;
read_request.output = reinterpret_cast<uint8_t*>(depth);
read_request.num_bytes = viewport.width * viewport.height * sizeof(float);
const FrameHandle frame =
FilamentContext::Render({&request, 1}, {&read_request, 1});
FilamentContext::Render({&render_requests_[0], 1}, {&read_request, 1});
FilamentContext::WaitForFrame(frame);
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
render_requests_[0].draw_mode = last_draw_mode;
}
}
@@ -175,10 +203,7 @@ void MjrFilamentRenderer::UploadHeightField(const mjModel* model, int id) {
uintptr_t MjrFilamentRenderer::UploadGuiImage(uintptr_t tex_id,
const uint8_t* pixels, int width,
int height, int bpp) {
if (imgui_bridge_) {
return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp);
}
return 0;
return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp);
}
void MjrFilamentRenderer::UpdateGui() { DrawGui(scene_bridge_.get()); }
@@ -25,7 +25,6 @@
#include "experimental/filament/filament/filament_context.h"
#include "experimental/filament/filament/imgui_bridge.h"
#include "experimental/filament/filament/scene_bridge.h"
#include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/render_context_filament.h"
namespace mujoco {
@@ -71,16 +70,14 @@ class MjrFilamentRenderer : public FilamentContext {
MjrFilamentRenderer& operator=(const MjrFilamentRenderer&) = delete;
private:
enum SwapChainType {
kWindowSwapChain,
kOffscreenSwapChain,
enum class FrameBufferMode {
Window,
OffScreen,
OffScreenWithGui,
};
DrawMode last_render_mode_ = DrawMode::Color;
mjvGLCamera last_camera_;
SwapChainType scene_swap_chain_target_ = kWindowSwapChain;
SwapChainType gui_swap_chain_target_ = kWindowSwapChain;
std::unique_ptr<SceneView> scene_view_;
FrameBufferMode mode_ = FrameBufferMode::Window;
RenderRequest render_requests_[2];
std::unique_ptr<SceneBridge> scene_bridge_;
std::unique_ptr<ImguiBridge> imgui_bridge_;
};
@@ -87,9 +87,9 @@ static std::unique_ptr<Texture> CreateFallbackIndirectLightTexture(
return texture;
}
SceneBridge::SceneBridge(ObjectManager* object_mgr, SceneView* scene_view,
const mjModel* model)
: scene_view_(scene_view), object_mgr_(object_mgr) {
SceneBridge::SceneBridge(ObjectManager* object_mgr, const mjModel* model)
: object_mgr_(object_mgr) {
scene_view_ = std::make_unique<SceneView>(object_mgr_->GetEngine());
model_objects_ =
std::make_unique<ModelObjects>(model, object_mgr_->GetEngine());
@@ -354,8 +354,16 @@ filament::math::mat4 CalculateClipFromWorld(const mjrRect& viewport,
}
void SceneBridge::Update(const mjrRect& viewport, const mjvScene* scene) {
filament::View* view = scene_view_->GetDefaultRenderView();
view->setShadowingEnabled(scene->flags[mjRND_SHADOW] ? true : false);
if (scene->flags[mjRND_SHADOW]) {
scene_view_->EnableShadows();
} else {
scene_view_->DisableShadows();
}
if (scene->flags[mjRND_REFLECTION]) {
scene_view_->EnableReflections();
} else {
scene_view_->DisableReflections();
}
mjtNum hpos[3], hfwd[3];
float headpos[3], gazedir[3];
@@ -25,7 +25,6 @@
#include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/filament/light.h"
#include "experimental/filament/filament/material.h"
#include "experimental/filament/filament/model_objects.h"
#include "experimental/filament/filament/object_manager.h"
#include "experimental/filament/filament/renderable.h"
@@ -37,8 +36,7 @@ namespace mujoco {
// Manages all mjModel data and updates a SceneView using an mjvScene.
class SceneBridge {
public:
SceneBridge(ObjectManager* object_mgr, SceneView* scene_view,
const mjModel* model);
SceneBridge(ObjectManager* object_mgr, const mjModel* model);
~SceneBridge();
// Updates the environment light using the KTX image at the given path.
@@ -56,7 +54,8 @@ class SceneBridge {
void UploadTexture(const mjModel* model, int id);
void UploadHeightField(const mjModel* model, int id);
SceneView* GetSceneView() const { return scene_view_; }
// Returns the managed scene.
SceneView* GetSceneView() const { return scene_view_.get(); }
SceneBridge(const SceneBridge&) = delete;
SceneBridge& operator=(const SceneBridge&) = delete;
@@ -69,7 +68,7 @@ class SceneBridge {
std::optional<filament::math::float3> ClipFromWorld(
const filament::math::float3& pos) const;
SceneView* scene_view_ = nullptr;
std::unique_ptr<SceneView> scene_view_;
ObjectManager* object_mgr_ = nullptr;
std::unique_ptr<ModelObjects> model_objects_;
std::unique_ptr<Light> fallback_ibl_;
@@ -351,7 +351,6 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
const mjModel* model = model_objs->GetModel();
const bool use_segid_color = scene->flags[mjRND_IDCOLOR];
const bool enable_reflection = scene->flags[mjRND_REFLECTION];
MaterialParams params;
params.color = ReadFloat4(geom.rgba);
if (geom.type == mjGEOM_PLANE) {
@@ -361,8 +360,7 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
params.reflective = false;
} else {
renderable.SetReceiveShadows(true);
params.reflective =
enable_reflection && geom.reflectance > 0 && params.color.a == 1.0f;
params.reflective = geom.reflectance > 0 && params.color.a == 1.0f;
}
}
renderable.SetLayerMask(geom.category);
@@ -127,9 +127,7 @@ static void SetupReflectionCamera(const mat4& surface_xform,
SceneView::SceneView(filament::Engine* engine) : engine_(engine) {
scene_ = engine->createScene();
ux_scene_ = engine->createScene();
camera_ = engine->createCamera(utils::EntityManager::get().create());
ux_camera_ = engine->createCamera(utils::EntityManager::get().create());
reflect_camera_ = engine->createCamera(utils::EntityManager::get().create());
for (auto& view : views_) {
@@ -139,12 +137,6 @@ SceneView::SceneView(filament::Engine* engine) : engine_(engine) {
view->setVisibleLayers(0xff, mjCAT_ALL);
}
ux_view_ = engine->createView();
ux_view_->setScene(ux_scene_);
ux_view_->setCamera(ux_camera_);
ux_view_->setPostProcessingEnabled(false);
ux_view_->setShadowingEnabled(false);
reflect_view_ = engine->createView();
reflect_view_->setScene(scene_);
reflect_view_->setCamera(reflect_camera_);
@@ -172,22 +164,16 @@ SceneView::~SceneView() {
for (auto& renderable : renderables_) {
renderable->RemoveFromScene(scene_);
}
for (auto& renderable : ux_renderables_) {
renderable->RemoveFromScene(ux_scene_);
}
lights_.clear();
renderables_.clear();
reflect_targets_.clear();
engine_->destroyCameraComponent(reflect_camera_->getEntity());
engine_->destroy(reflect_view_);
engine_->destroyCameraComponent(ux_camera_->getEntity());
engine_->destroy(ux_view_);
engine_->destroyCameraComponent(camera_->getEntity());
if (color_grading_) {
engine_->destroy(color_grading_);
}
engine_->destroy(scene_);
engine_->destroy(ux_scene_);
for (auto& view : views_) {
engine_->destroy(view);
}
@@ -224,18 +210,6 @@ void SceneView::RemoveFromScene(Renderable* renderable) {
}
}
void SceneView::AddToUxScene(Renderable* renderable) {
if (ux_renderables_.insert(renderable).second) {
renderable->AddToScene(ux_scene_);
}
}
void SceneView::RemoveFromUxScene(Renderable* renderable) {
if (ux_renderables_.erase(renderable)) {
renderable->RemoveFromScene(ux_scene_);
}
}
void SceneView::AddToScene(filament::Skybox* skybox) {
skybox_ = skybox;
scene_->setSkybox(skybox);
@@ -255,7 +229,6 @@ void SceneView::Render(filament::Renderer* renderer,
for (auto& view : views_) {
view->setViewport(viewport);
}
ux_view_->setViewport(viewport);
reflect_view_->setViewport(viewport);
SetupCamera(request.camera, viewport, camera_);
@@ -276,7 +249,7 @@ void SceneView::Render(filament::Renderer* renderer,
}
// Render reflection passes.
if (request.draw_mode == DrawMode::Color) {
if (request.draw_mode == DrawMode::Color && reflections_enabled_) {
for (size_t i = 0; i < reflectives_.size(); ++i) {
Renderable* renderable = reflectives_[i];
@@ -301,15 +274,6 @@ void SceneView::Render(filament::Renderer* renderer,
renderer->render(view);
view->setRenderTarget(nullptr);
if (request.enable_ux) {
ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f,
viewport.width, viewport.height, 0.0f, 0.0f,
1.0f);
ux_view_->setRenderTarget(render_target);
renderer->render(ux_view_);
ux_view_->setRenderTarget(nullptr);
}
if (request.target) {
view->setMultiSampleAntiAliasingOptions(options);
}
@@ -335,9 +299,11 @@ void SceneView::AddReflectiveRenderable(Renderable* renderable) {
auto& target = reflect_targets_[index];
target->Prepare(viewport.width, viewport.height);
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = target->GetColorTexture();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
if (reflections_enabled_) {
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = target->GetColorTexture();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
}
void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) {
@@ -353,6 +319,43 @@ void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) {
color_grading_options_ = opts;
}
void SceneView::EnableShadows() {
views_[kNormalIndex]->setShadowingEnabled(true);
}
void SceneView::DisableShadows() {
views_[kNormalIndex]->setShadowingEnabled(false);
}
void SceneView::EnableReflections() {
reflections_enabled_ = true;
for (int i = 0; i < reflectives_.size(); ++i) {
Renderable* renderable = reflectives_[i];
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = reflect_targets_[i]->GetColorTexture();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
}
void SceneView::DisableReflections() {
reflections_enabled_ = false;
for (Renderable* renderable : reflectives_) {
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = nullptr;
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
}
void SceneView::EnablePostProcessing() {
views_[kNormalIndex]->setPostProcessingEnabled(true);
}
void SceneView::DisablePostProcessing() {
views_[kNormalIndex]->setPostProcessingEnabled(false);
}
filament::View* SceneView::GetDefaultRenderView() {
return views_[kNormalIndex];
}
+14 -14
View File
@@ -38,8 +38,7 @@ namespace mujoco {
//
// The filament Scene is populated with the objects (e.g. lights, renderables,
// skybox, etc.). It manages multiple views to support a variety of draw modes
// (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces. It
// also manages a separate scene and view for UX rendering.
// (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces.
class SceneView {
public:
SceneView(filament::Engine* engine);
@@ -53,10 +52,6 @@ class SceneView {
void AddToScene(filament::Skybox* skybox);
void RemoveFromScene(filament::Skybox* skybox);
// Adds/removes entities from the UX scene, which is rendered separately.
void AddToUxScene(Renderable* renderable);
void RemoveFromUxScene(Renderable* renderable);
// Parameters for rendering the scene.
struct RenderRequest {
// The draw mode (e.g. normal, depth, segmentation) to render.
@@ -67,8 +62,6 @@ class SceneView {
mjvGLCamera camera;
// An optional render target into which the scene will be rendered.
RenderTarget* target = nullptr;
// Whether or not to render the UX as a separate pass.
bool enable_ux = false;
};
// Renders the scene.
@@ -77,6 +70,18 @@ class SceneView {
// Returns the filament Engine managing the scene.
filament::Engine* GetEngine() const { return engine_; }
// Enables/disables shadows for the default render view.
void EnableShadows();
void DisableShadows();
// Enables/disables reflections for the default render view.
void EnableReflections();
void DisableReflections();
// Enables/disables post processing for the default render view.
void EnablePostProcessing();
void DisablePostProcessing();
// Returns the underlying filament View that is used for normal rendering.
// Callers can update rendering settings (e.g. post processing) directly.
filament::View* GetDefaultRenderView();
@@ -95,7 +100,6 @@ class SceneView {
filament::Engine* engine_ = nullptr;
filament::Scene* scene_ = nullptr;
filament::Scene* ux_scene_ = nullptr;
filament::Camera* camera_ = nullptr;
filament::ColorGrading* color_grading_ = nullptr;
ColorGradingOptions color_grading_options_;
@@ -106,16 +110,12 @@ class SceneView {
std::unordered_set<Renderable*> renderables_;
filament::Skybox* skybox_ = nullptr;
// Custom view for UX.
filament::View* ux_view_ = nullptr;
filament::Camera* ux_camera_ = nullptr;
std::unordered_set<Renderable*> ux_renderables_;
// Custom view and camera for reflective surfaces.
filament::View* reflect_view_ = nullptr;
filament::Camera* reflect_camera_ = nullptr;
// The list of reflective renderables and their corresponding render targets.
bool reflections_enabled_ = true;
std::vector<Renderable*> reflectives_;
std::vector<std::unique_ptr<RenderTarget>> reflect_targets_;
};