Skip to main content

mujoco-rendering

Offscreen image rendering, Python Renderer class, C++ pipeline (mjrContext, mjvScene, mjr_render, mjr_readPixels), RGB, metric depth maps, segmentation masks (mjtObj), offwidth/offheight framebuffer sizing, MUJOCO_GL (osmesa, egl), cameras (fixed, tracking, free, MjvCamera), scene options (geomgroup, flags). Use for headless camera rendering, dataset recording, visual observations. Do NOT use for interactive viewers/Studio (mujoco-studio) or ImGui controls (mujoco-gui).

Source facts

Repository
google-deepmind/mujoco
Last source activity
November 5, 2026 at 12:37
Detected SKILL.md language
English
Stars
16,072
Forks
1,925

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
mujoco-rendering
description
Offscreen image rendering, Python Renderer class, C++ pipeline (mjrContext, mjvScene, mjr_render, mjr_readPixels), RGB, metric depth maps, segmentation masks (mjtObj), offwidth/offheight framebuffer sizing, MUJOCO_GL (osmesa, egl), cameras (fixed, tracking, free, MjvCamera), scene options (geomgroup, flags). Use for headless camera rendering, dataset recording, visual observations. Do NOT use for interactive viewers/Studio (mujoco-studio) or ImGui controls (mujoco-gui).
# MuJoCo Rendering: Cameras, Contexts & Offscreen Buffers > [!TIP] > > **Official MuJoCo documentation (`doc/*.rst`):** Read > [`../../programming/rendering.rst`](../../programming/rendering.rst) > (`.. _Rendering:`, `.. _FilamentRendering:`, `.. _ClassicRendering:`) and > [`../../programming/visualization.rst`](../../programming/visualization.rst) > (`.. _Visualization:`) directly when in a MuJoCo checkout; for large > reference files like [`../../python.rst`](../../python.rst) (`.. _PyRender:`), > [`../../XMLreference.rst`](../../XMLreference.rst), and > [`../../APIreference/functions.rst`](../../APIreference/functions.rst), grep > the literal `.. _Label:` anchor. Outside a checkout, the same `doc/*.rst` tree > ships offline in `pip install mujoco` at > `pathlib.Path(mujoco.__file__).parent / 'doc'`; otherwise fetch > `https://mujoco.readthedocs.io/en/latest/<path>.html` (or `/en/stable/` for > released pages; `functions.rst` renders inside > `APIreference/APIfunctions.html`, and Sphinx anchors lowercase the label, > e.g. `.. _PyRender:` → `#pyrender`) and browse repo paths (`../../../`) under > `https://github.com/google-deepmind/mujoco/tree/main/`. > > **Related skills:** > > - [mujoco-python](../python/SKILL.md) — Core physics simulation, stepping, > and state management. > - [mujoco-studio](../studio/SKILL.md) — Interactive simulation, Studio > viewer apps, and plugin architecture. > - [mujoco-gui](../gui/SKILL.md) — Dear ImGui UI design and custom controls. ## 1. Overview: Rendering Backends & Workflows MuJoCo provides two built-in 3D rendering backends (compared side-by-side in [programming/rendering.rst](../../programming/rendering.rst), `.. _Rendering:`): - **Classic OpenGL (`mujoco.Renderer` / C `mjr`)**: Immediate-mode OpenGL 1.5 renderer (`mjv_updateScene` -> `mjr_render` -> `mjr_readPixels`), now in maintenance mode. Bundled out of the box in `pip install mujoco`, making `mujoco.Renderer` the simplest single-class helper for headless RGB, depth, and segmentation capture. See [programming/rendering.rst](../../programming/rendering.rst) (`.. _ClassicRendering:`) and [python.rst](../../python.rst) (`.. _PyClassicRender:`). - **Filament PBR (`mujoco.mjrf` & `mujoco.rendering.filament` / C `mjrf`)**: Retained-mode, asynchronous physically-based renderer supporting OpenGL, Vulkan, Metal, and WebGL (owns its own headless or windowed GPU context and arbitrary-size `mjrfRenderTarget` buffers). Recommended for PBR materials, image-based/photometric lighting, and new C++/Studio applications; see §7 and [programming/rendering.rst](../../programming/rendering.rst) (`.. _FilamentRendering:`). - **Interactive Viewers (`mujoco.studio` / `mujoco.viewer`)**: Desktop or web-streamed interactive viewers; use [`mujoco-studio`](../studio/SKILL.md) instead. ## 2. Headless Contexts on Linux: `MUJOCO_GL` On headless Linux systems (servers, containers), configure the OpenGL context provider via the `MUJOCO_GL` environment variable **before importing `mujoco`**: ```python import os os.environ['MUJOCO_GL'] = 'egl' # Hardware-accelerated offscreen on NVIDIA GPUs # os.environ['MUJOCO_GL'] = 'osmesa' # Portable CPU software rasterization import mujoco # Must import AFTER setting MUJOCO_GL ``` Or from the command line: ```bash MUJOCO_GL=egl python3 my_script.py ``` ## 3. The Python Renderer Class (RGB, Depth & Segmentation) `mujoco.Renderer` manages GL context creation, scene geometry synchronization, and buffer readback. **Always use it as a context manager**: ```python import mujoco import numpy as np model = mujoco.MjModel.from_xml_path('scene.xml') data = mujoco.MjData(model) with mujoco.Renderer(model, height=480, width=640) as renderer: mujoco.mj_forward(model, data) # 1. RGB Rendering renderer.update_scene(data, camera='overhead_cam') rgb = renderer.render() # Shape: (480, 640, 3), dtype: uint8 # 2. Metric Depth Rendering (distance in meters) renderer.enable_depth_rendering() renderer.update_scene(data, camera='overhead_cam') depth = renderer.render() # Shape: (480, 640), dtype: float32 renderer.disable_depth_rendering() # 3. Semantic Segmentation Rendering renderer.enable_segmentation_rendering() renderer.update_scene(data, camera='overhead_cam') seg = renderer.render() # Shape: (480, 640, 2), dtype: int32 # seg[:, :, 0] = object ID (-1 for background) # seg[:, :, 1] = object type (mjtObj enum, e.g. mjOBJ_GEOM) renderer.disable_segmentation_rendering() ``` ### Rendering Inside Simulation Loops Creating a `Renderer` allocates GPU framebuffers. In RL or evaluation loops, **create the renderer once outside the loop**: ```python with mujoco.Renderer(model, height=84, width=84) as renderer: for step in range(1000): mujoco.mj_step(model, data) if step % render_interval == 0: renderer.update_scene(data, camera='agent_cam') # Call .copy() because the renderer's internal buffer is reused frame = renderer.render().copy() ``` ## 4. C++ Offscreen Rendering Pipeline (mjr & mjv) In C++, offscreen rendering is performed using the `mjvScene` and `mjrContext` APIs: ```cpp #include <mujoco/mujoco.h> // 1. Initialize visualization structures mjvCamera cam; mjv_defaultCamera(&cam); cam.type = mjCAMERA_FIXED; cam.fixedcamid = mj_name2id(m, mjOBJ_CAMERA, "overhead_cam"); mjvOption opt; mjv_defaultOption(&opt); mjvScene scn; mjv_makeScene(m, &scn, 2000); // Buffer up to 2000 geoms // 2. Initialize offscreen rendering context mjrContext con; mjr_defaultContext(&con); mjr_makeContext(m, &con, mjFONTSCALE_100); // 3. Render frame mjrRect viewport = {0, 0, 640, 480}; mjv_updateScene(m, d, &opt, nullptr, &cam, mjCAT_ALL, &scn); mjr_render(viewport, &scn, &con); // 4. Read pixels into user buffers unsigned char rgb[640 * 480 * 3]; float depth[640 * 480]; mjr_readPixels(rgb, depth, viewport, &con); // 5. Clean up mjr_freeContext(&con); mjv_freeScene(&scn); ``` ## 5. Camera Definition & Placement MuJoCo cameras look down their **negative Z-axis**: - **-Z**: Viewing forward direction - **+X**: Right - **+Y**: Up ### Defining Cameras in MjSpec ```python # Fixed camera pointing down at ground plane cam = spec.worldbody.add_camera( name='overhead', pos=[0, 0, 3.0], quat=[0.707, 0.707, 0, 0], # 90 deg rotation around X fovy=60, ) # Camera attached to robot wrist wrist_cam = wrist_body.add_camera( name='wrist_cam', pos=[0.05, 0, 0], xyaxes=[0, -1, 0, 0, 0, -1], fovy=75, ) ``` ### Free Camera (`MjvCamera`) For custom interactive or orbital viewpoints: ```python cam = mujoco.MjvCamera() cam.type = mujoco.mjtCamera.mjCAMERA_FREE cam.lookat[:] = [0, 0, 0.5] cam.distance = 2.5 cam.azimuth = 90.0 cam.elevation = -20.0 renderer.update_scene(data, camera=cam) ``` ## 6. Framebuffers & Scene Options ### Offscreen Framebuffer Resolution The offscreen rendering resolution cannot exceed the dimensions pre-allocated in the model. Adjust the buffer limit in the spec before compilation: ```python spec.visual.global_.offwidth = 1920 spec.visual.global_.offheight = 1080 model = spec.compile() ``` ### The Single `<visual>` Block Rule MuJoCo uses only the **last** `<visual>` block defined in an XML file. Multiple visual blocks will silently overwrite earlier settings: ```xml <!-- ❌ WRONG — second visual block silently resets offwidth/offheight to default --> <visual><global offwidth="1920" offheight="1080"/></visual> <visual><headlight ambient="0.3 0.3 0.3"/></visual> <!-- ✅ RIGHT — consolidate into one visual block --> <visual> <global offwidth="1920" offheight="1080"/> <headlight ambient="0.3 0.3 0.3"/> </visual> ``` ### Geom Group Visibility & Visualization Flags Control rendering layers via `MjvOption`: ```python opt = mujoco.MjvOption() opt.geomgroup[:] = False # Hide all groups opt.geomgroup[0] = True # Show primary geometry (default group) opt.geomgroup[1] = True # Show secondary visuals # Toggle visual debugging flags opt.flags[mujoco.mjtVisFlag.mjVIS_CONTACTFORCE] = True opt.flags[mujoco.mjtVisFlag.mjVIS_TRANSPARENT] = True renderer.update_scene(data, camera='overhead', scene_option=opt) ``` ## 7. Filament Physically-Based Rendering (`mjrf`) MuJoCo's Filament backend (`mjrfilament.h` in C/C++, `from mujoco import mjrf` and `mujoco.rendering.filament` in Python) provides retained-mode PBR with OpenGL, Vulkan, Metal, and WebGL support, arbitrary `mjrfRenderTarget` sizes (independent of `<global offwidth offheight/>`), and asynchronous `mjrfReadRequest` pixel readback: - **Python (`ModelScene` + `Renderer`)**: Create an `mjrf.Context`, bind model lights and renderables once via `mujoco.rendering.filament.model_scene.ModelScene`, update in place with `ms.update(data)`, and render into a NumPy target buffer via `mujoco.rendering.filament.renderer.Renderer` — see the complete example in [python.rst](../../python.rst) (`.. _PyFilamentRender:`). - **C/C++ & CMake**: Build with `-DMUJOCO_USE_FILAMENT=ON` (or `-DMUJOCO_USE_FILAMENT_MJR_COMPAT=ON` for drop-in `mjr_` compatibility) and consult [programming/rendering.rst](../../programming/rendering.rst) (`.. _reProgramming:`, `.. _reSupport:`). - **Photometric light-intensity gotcha**: If *any* `<light>` has positive `intensity`, Filament switches the entire scene from classic headlight + default environment lighting to photometric mode (turning off the headlight and darkening zero-intensity lights). For PBR materials, textures, and lighting authoring, read [programming/rendering.rst](../../programming/rendering.rst) (`.. _reAuthoring:`, `.. _reMaterials:`, `.. _reLighting:`) and inspect the reference PBR/IBL test scenes in `../../../test/render/testdata/` (`pbr.xml`, `lighting.xml`, `emissive_bloom.xml`, `reflect_pbr.xml`, `ibl.ktx`) and `../../../model/mug/mug.xml`. ## 8. Common Gotchas & Best Practices 1. **GL context leaks**: Always close `mujoco.Renderer` or use a `with` block. Failing to free renderers in loops exhausts GL contexts. 2. **Destruction order**: In C++, `mjr_freeContext` must be called before the underlying OpenGL context is destroyed (see [programming/rendering.rst](../../programming/rendering.rst), `.. _reContext:`). 3. **Buffer re-use**: `renderer.render()` returns an internal buffer view. Call `.copy()` if storing frames in a list. 4. **Camera lookup by name**: Pass `camera='camera_name'` or the integer ID. Avoid passing raw string IDs that are not present in the model. ## 9. Key References ### Documentation - [Rendering Guide (Filament `mjrf` & Classic OpenGL `mjr`)](../../programming/rendering.rst)
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub