{
"cells": [
{
"cell_type": "markdown",
"id": "ecab2f91",
"metadata": {},
"source": [
"[](https://colab.research.google.com/github/tayalmanan28/MuJoCo-Tutorial/blob/main/tutorial/02_rendering.ipynb)\n",
"\n",
"# Tutorial 2: Rendering\n",
"\n",
"Learn to render scenes, configure cameras, and create videos."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a9b5f52",
"metadata": {},
"outputs": [],
"source": [
"!pip install -q mujoco mediapy"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "55aae499",
"metadata": {},
"outputs": [],
"source": [
"import mujoco\n",
"import numpy as np\n",
"import mediapy as media"
]
},
{
"cell_type": "markdown",
"id": "b4674b30",
"metadata": {},
"source": [
"## Basic Rendering with `mujoco.Renderer`\n",
"\n",
"The `Renderer` class handles offscreen rendering — perfect for notebooks and headless servers."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5d9008eb",
"metadata": {},
"outputs": [],
"source": [
"xml = \"\"\"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"\n",
"\"\"\"\n",
"\n",
"model = mujoco.MjModel.from_xml_string(xml)\n",
"data = mujoco.MjData(model)\n",
"mujoco.mj_forward(model, data)\n",
"\n",
"# Render\n",
"renderer = mujoco.Renderer(model, height=480, width=640)\n",
"renderer.update_scene(data)\n",
"media.show_image(renderer.render())"
]
},
{
"cell_type": "markdown",
"id": "f615aa37",
"metadata": {},
"source": [
"## Camera Configuration\n",
"\n",
"You can control the viewpoint by configuring the camera."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "22dc1158",
"metadata": {},
"outputs": [],
"source": [
"# Create a camera and set its parameters\n",
"camera = mujoco.MjvCamera()\n",
"camera.azimuth = 135\n",
"camera.elevation = -20\n",
"camera.distance = 2.0\n",
"camera.lookat[:] = [0, 0, 0.2]\n",
"\n",
"renderer.update_scene(data, camera=camera)\n",
"media.show_image(renderer.render())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bf8df6d7",
"metadata": {},
"outputs": [],
"source": [
"# Render from multiple viewpoints\n",
"images = []\n",
"for azimuth in [0, 90, 180, 270]:\n",
" camera.azimuth = azimuth\n",
" renderer.update_scene(data, camera=camera)\n",
" images.append(renderer.render().copy())\n",
"\n",
"media.show_images(images, titles=['Front', 'Right', 'Back', 'Left'])"
]
},
{
"cell_type": "markdown",
"id": "1707f16e",
"metadata": {},
"source": [
"## Fixed Cameras in XML\n",
"\n",
"You can also define cameras directly in the model:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2b60f04c",
"metadata": {},
"outputs": [],
"source": [
"xml_with_cameras = \"\"\"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"\n",
"\"\"\"\n",
"\n",
"model2 = mujoco.MjModel.from_xml_string(xml_with_cameras)\n",
"data2 = mujoco.MjData(model2)\n",
"mujoco.mj_forward(model2, data2)\n",
"\n",
"renderer2 = mujoco.Renderer(model2, height=300, width=400)\n",
"\n",
"images = []\n",
"for cam_name in ['top_down', 'side']:\n",
" cam_id = mujoco.mj_name2id(model2, mujoco.mjtObj.mjOBJ_CAMERA, cam_name)\n",
" renderer2.update_scene(data2, camera=cam_id)\n",
" images.append(renderer2.render().copy())\n",
"\n",
"media.show_images(images, titles=['Top-down', 'Side'])\n",
"renderer2.close()"
]
},
{
"cell_type": "markdown",
"id": "6d1eda1b",
"metadata": {},
"source": [
"## Creating Videos\n",
"\n",
"Render frames during simulation and combine into a video:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "49aee748",
"metadata": {},
"outputs": [],
"source": [
"xml_ball = \"\"\"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"\n",
"\"\"\"\n",
"\n",
"model3 = mujoco.MjModel.from_xml_string(xml_ball)\n",
"data3 = mujoco.MjData(model3)\n",
"renderer3 = mujoco.Renderer(model3, height=480, width=640)\n",
"\n",
"# Give initial velocity\n",
"data3.qvel[0] = 3.0 # horizontal\n",
"\n",
"frames = []\n",
"fps = 60\n",
"duration = 2.0\n",
"\n",
"while data3.time < duration:\n",
" mujoco.mj_step(model3, data3)\n",
" if len(frames) < data3.time * fps:\n",
" renderer3.update_scene(data3)\n",
" frames.append(renderer3.render().copy())\n",
"\n",
"renderer3.close()\n",
"media.show_video(frames, fps=fps)"
]
},
{
"cell_type": "markdown",
"id": "1d99e86a",
"metadata": {},
"source": [
"## Depth and Segmentation Rendering"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "47b5c5f7",
"metadata": {},
"outputs": [],
"source": [
"model4 = mujoco.MjModel.from_xml_string(xml)\n",
"data4 = mujoco.MjData(model4)\n",
"mujoco.mj_forward(model4, data4)\n",
"\n",
"renderer4 = mujoco.Renderer(model4, height=480, width=640)\n",
"\n",
"# RGB\n",
"renderer4.update_scene(data4)\n",
"rgb = renderer4.render().copy()\n",
"\n",
"# Depth\n",
"renderer4.enable_depth_rendering()\n",
"renderer4.update_scene(data4)\n",
"depth = renderer4.render().copy()\n",
"renderer4.disable_depth_rendering()\n",
"\n",
"# Segmentation\n",
"renderer4.enable_segmentation_rendering()\n",
"renderer4.update_scene(data4)\n",
"seg = renderer4.render()[:, :, 0].copy() # geom IDs in first channel\n",
"renderer4.disable_segmentation_rendering()\n",
"\n",
"renderer4.close()\n",
"\n",
"import matplotlib.pyplot as plt\n",
"fig, axes = plt.subplots(1, 3, figsize=(15, 4))\n",
"axes[0].imshow(rgb); axes[0].set_title('RGB')\n",
"axes[1].imshow(depth, cmap='viridis'); axes[1].set_title('Depth')\n",
"axes[2].imshow(seg, cmap='tab10'); axes[2].set_title('Segmentation')\n",
"for ax in axes: ax.axis('off')\n",
"plt.tight_layout()\n",
"plt.show()"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}