{ "cells": [ { "cell_type": "markdown", "id": "ecab2f91", "metadata": {}, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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", "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 }