Skip to main content

isaac-sim-rendering

Headless RT2/PathTracing production rendering with ACES tuning. Use when capturing frames or validating render quality.

소스 정보

저장소
isaac-sim/IsaacSim
최근 소스 활동
2026년 9월 18일 16:05
감지된 SKILL.md 언어
영어
스타
4,169
포크
560

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
5 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
isaac-sim-rendering
description
Headless RT2/PathTracing production rendering with ACES tuning. Use when capturing frames or validating render quality.
license
Apache-2.0
metadata
{"author":"Renato Gasoto <info@nvidia.com>"}
# Isaac Sim Headless Rendering (Kit 110 / Isaac Sim 6.0+) ## Purpose Capture production-quality headless frames with RT2 or PathTracing, ACES tone mapping, warehouse lighting patterns, and quantitative validation thresholds. ## Prerequisites - Built Isaac Sim (`$ISAAC_SIM_DIR` or `_build/linux-x86_64/release`). - NVIDIA GPU with a current driver (`nvidia-smi`). - Shell env contract from `isaac-sim-orchestrator`: `$ISAAC_SIM_DIR`, `$ISAAC_LAB_DIR`, `$WORKSPACE_DIR`. ## Limitations - Targets Isaac Sim 6 / Kit 110 unless a section states otherwise. - Does not replace official NVIDIA documentation for unsupported edge cases. ## Troubleshooting | Error / symptom | Cause | Solution | |---|---|---| | Extension or import not found | Wrong `$ISAAC_SIM_DIR` or stale build | Point env vars at `_build/linux-x86_64/release` or rebuild | | Black or empty frames | Missing lights or non-RTX render mode | Add dome/key light; confirm RTX / PathTracing settings | | Hang on stage load or first render | MDL compile or oversized stage | Follow isolation steps in `isaac-sim-troubleshooting` | Capture pipeline, lighting recipes, ACES calibration, camera math, validation. Host-agnostic; adapt paths to your environment. ## Available Scripts | Script | Purpose | Arguments | |---|---|---| | `scripts/capture_pipeline.py` | Standard Kit 110 / Isaac Sim 6.0+ headless capture pipeline | see script --help | | `scripts/look_at_camera.py` | Look-at camera math for USD cameras (Z-up, USD -Z forward convention) | see script --help | | `scripts/warehouse_lighting.py` | Multi-layer warehouse lighting recipes for headless Isaac Sim rendering | see script --help | ## Running scripts From agent runtimes that expose skill execution helpers, invoke helpers with `run_script()`: ```python run_script("scripts/capture_pipeline.py", args=["--help"]) ``` From a built Isaac Sim tree, run the same file with `./python.sh` (Linux) or `python.bat` (Windows) from `_build/*/release`, or execute shell helpers directly when they do not require the simulator. ## Read first - `navigation-primitives`: look-at chase camera math (cross-referenced). ## Capture: SimulationApp + Replicator RGB Annotator Standard Kit 110 / Isaac Sim 6.0+ capture pipeline. Works headless including on ARM64 / GB10 Spark (the older 5.1.0 black-frame bug is resolved). `setup_capture_pipeline(stage_path, width, height, renderer, settle_frames)` — open stage, define camera, attach RGB annotator, settle, return `(app, rgb_annot, render_product)`. `capture_frame(rgb_annot)` — step replicator and return `(H, W, 3)` uint8 RGB array. See [`scripts/capture_pipeline.py`](scripts/capture_pipeline.py). For live controller demos where simulation remains the time authority, disable Replicator capture-on-play and capture snapshots without pausing the timeline. See [`scripts/capture_pipeline.py`](scripts/capture_pipeline.py) for the executable pattern. Inside a Python-server file that is otherwise synchronous, avoid the sync `rep.orchestrator.step()` call in live Kit. Schedule `step_async(...)` and pump `app_utils.update_app()` until the task completes; this prevents event-loop reentrancy failures while still advancing the render product. **Capture method choice**: - `omni.replicator.core` RGB annotator -> reliable, supports any resolution. - `RtxCamera` + `CameraSensor` (from `isaacsim.sensors.experimental.rtx`) for tick-rate control, OpenCV / fisheye lens distortion, ISP, tiled multi-view, or stereo depth (see `isaac-camera`). - Swapchain capture -> also works on Kit 110 if you explicitly set window size matching the render resolution. - Replicator render products may return empty arrays for Gaussian splat scenes; fall back to swapchain capture in that case. ## RT2 vs PathTracing ```python settings.set("/rtx/rendermode", "RayTracedLighting") # RT2 — real-time # settings.set("/rtx/rendermode", "PathTracing") # offline only ``` | Mode | Convergence | Per-frame time | Use for | |---|---|---|---| | **RayTracedLighting (RT2)** | ~200 settle frames (~10-15s) | 10-15s | All iterative work, warehouse scenes, training data | | **PathTracing** | converges over many subframes | 5-30 min | Final hero shots only, when explicitly requested | **Default to RT2.** Switch to PathTracing only after RT2 has been calibrated and the user asks for hero quality. ## Headless Lighting — Add Explicit Lights Headless Isaac Sim has **NO default lighting**. Without explicit lights, frames are black (RGB=0). Always inject at least a `DomeLight` + `DistantLight` baseline. ```python from pxr import UsdLux, UsdGeom, Gf dome = UsdLux.DomeLight.Define(stage, "/World/DomeLight") dome.GetIntensityAttr().Set(400.0) sun = UsdLux.DistantLight.Define(stage, "/World/Sun") sun.GetIntensityAttr().Set(1500.0) UsdGeom.Xformable(sun.GetPrim()).AddRotateXYZOp().Set(Gf.Vec3f(-50, 20, 0)) ``` ### Baseline Intensity Guide | Scene Type | DomeLight | DistantLight | Notes | |---|---|---|---| | Warehouse (default) | 400 | 1500 | Good general balance | | Close-up robot | 300 | 1200 | Slightly softer | | Outdoor | 500 | 2000 | Brighter sun | | Dark/moody | 100 | 800 | Dramatic shadows | ## ACES Tone Mapping — The Single Biggest Quality Lever Without ACES, no amount of intensity tuning produces balanced indoor renders. This is the single most impactful render setting after lighting. ```python import carb s = carb.settings.get_settings() s.set("/rtx/post/tonemap/op", 4) # ACES s.set("/rtx/post/tonemap/filmIso", 600.0) # key parameter (see table) s.set("/rtx/post/tonemap/whitepoint", 6500.0) s.set("/rtx/post/tonemap/enabled", True) s.set("/rtx/post/aa/op", 3) # TAA for RT2 ``` ### filmIso Calibration (validated on warehouse interiors) | Scene | filmIso | Notes | |---|---|---| | General warehouse RT2 | 200 | Photorealistic starting point | | Deep-aisle indoor (hero camera) | 600 | Best balance across hero/overview/aisle/topdown | | Aerial/overview-heavy | 400 | Avoid overexposure on open views | ### Anti-Recipes (don't waste time on these) - Wide rect lights (width=5+) → flat, no light pools - High dome intensity (400+) with ACES filmIso 600 → washes out shadows - Reinhard tonemapping → muddy, low contrast - PathTracing for iterative work → 5-30 min per frame, kills velocity ## Warehouse Lighting Recipe (proven 7/10 → 9/10) The biggest single quality improvement came from this lighting + fog recipe. `add_warehouse_lighting(stage, n_lights, settings)` — low-ambient dome + focused rect lights + optional fog. Pass `settings=carb.settings.get_settings()` to enable fog. See [`scripts/warehouse_lighting.py`](scripts/warehouse_lighting.py). For 40m warehouse: fog density 0.003 adds depth without murk. ## Deep-Aisle Indoor Lighting ### Problem Ground-level camera in narrow aisle = black frame (82KB / mean_RGB < 5). Ceiling rect lights at Z=10m can't illuminate a 3.5m-wide × 8m-tall aisle to ground level — RT2 struggles with deep occlusion. ### Solution: Multi-Layer Lighting ```python # Layer 1: dense ceiling grid (6×12 across facility) # Rect lights at ceil_z-0.3, pointing down # intensity=200000, width=4.0, height=3.0 (wide coverage) # Layer 2: low sphere lights IN each aisle at Z=3.5m (head-height) # Directly in camera FOV between tier 1 and ground for aisle_y, lx in aisle_light_positions: lt = UsdLux.SphereLight.Define(stage, lp) lt.GetRadiusAttr().Set(0.15) lt.GetIntensityAttr().Set(100000.0) ``` - **500 settle frames** for indoor aisle scenes (not 200-300) - Dome at 300 intensity is optional ambient fill — don't go higher or open views wash out ### Dome vs Deep-Aisle Tension (fundamental conflict in enclosed scenes) - High dome → overview/topdown overexpose (mean > 220) - Low/no dome → deep aisle underexpose (mean < 10) - **Best balance**: no dome + sphere lights in aisles + 500K rect grids + 500 settle frames - Hero aisle: mean ~60 - Overview (elevated 3/4): mean ~140-175 - Cross-aisle: mean ~230 ### Validated ACES filmIso=600 Light Intensities - Ceiling rect lights: **70,000** intensity, 2.5×1.5m, warm white (1.0, 0.97, 0.92) - Aisle sphere lights: **15,000** intensity, radius=0.1, at Z=3.5m - Grid: 8×14 ceiling panels - **No dome light** — ACES handles exposure - Result: mean 60–155 across all view types **Camera tip**: place "hero" camera at cross-aisle intersections, not deep in narrow aisles. The junction has more open space for light to reach. ## Frame Quality Validation Always validate captured frames before delivery. Don't ship black/overexposed frames. | Indicator | Meaning | Action | |---|---|---| | File ~82KB | Black frame (RGBA padding only) | Add explicit lights | | File 200–500KB | Partial render / very simple scene | Check settle frames | | File 1–2MB | Full rendered frame | OK | | `rgb.max() == 0` | No lighting reaching camera | Add `DomeLight + DistantLight` | | `rgb.max() > 200`, mean 60–180 | Good render | OK | | `rgb.mean() > 220` | Overexposed | Reduce light intensity or filmIso | | `rgb.mean() < 10` | Underexposed | Add aisle-level lights or raise filmIso | ```python import numpy as np def validate_frame(rgb_array): """Returns (ok: bool, reason: str).""" if rgb_array.max() == 0: return False, "no light reaches camera — add DomeLight + DistantLight" if rgb_array.mean() > 220: return False, f"overexposed (mean={rgb_array.mean():.0f}) — reduce intensity" if rgb_array.mean() < 10: return False, f"underexposed (mean={rgb_array.mean():.0f}) — add aisle lights" return True, f"ok (mean={rgb_array.mean():.0f}, max={rgb_array.max()})" ``` ## Look-At Camera Math For chase/POV/overview cameras pointing at a target, always use a look-at matrix. Don't hand-tune Euler angles — they're brittle and you'll waste hours on sign flips. `look_at_matrix(eye, target, up)` — returns `Gf.Matrix4d` for a USD camera at `eye` looking at `target`. Handles degenerate up-vector (straight down/up). See [`scripts/look_at_camera.py`](scripts/look_at_camera.py). ### Third-Person Camera Offsets (Z-up, robot facing +X at yaw=0) | Direction | Vector | |---|---| | Behind robot | `-X` | | Right of robot | `-Y` | | Left of robot | `+Y` | | Above robot | `+Z` | ```python import math behind_dir_x = -math.cos(yaw) behind_dir_y = -math.sin(yaw) right_dir_x = -math.sin(yaw) right_dir_y = math.cos(yaw) cam_x = robot_x + behind_dist * behind_dir_x + side_offset * right_dir_x cam_y = robot_y + behind_dist * behind_dir_y + side_offset * right_dir_y cam_z = height ``` - `side_offset = -2.5` → camera on robot's **right** - `side_offset = +2.5` → camera on robot's **left** - Flip the *offset value* to change sides, NOT the trig signs. ## Dynamic Camera Height (Obstacle Avoidance) When tracking through cluttered environments, the chase camera will clip into tall geometry. Pre-compute obstacle bboxes, then raise the camera each frame as needed. ```python # Build obstacle lookup from USD geometry once at startup obstacles = [] for prim in stage.Traverse(): if prim.IsA(UsdGeom.Cube): # ... extract (xmin, xmax, ymin, ymax, height) ... obstacles.append((xmin, xmax, ymin, ymax, height)) def cam_max_height_at(cx, cy, margin=0.5): """Highest obstacle near (cx, cy). Camera must clear this.""" return max((h for xmn, xmx, ymn, ymx, h in obstacles if xmn-margin <= cx <= xmx+margin and ymn-margin <= cy <= ymx+margin), default=0.0) # Per-frame: target_h = max(base_height, cam_max_height_at(cam_x, cam_y) + 1.0) smooth_h = smooth_h * 0.95 + target_h * 0.05 # smooth transitions ``` ## Robot XformOp Discipline URDF-imported robots (Spot, Carter, etc.) already have authored `translate + orient + scale` xformOps on the root prim. - Use `xf.ClearXformOpOrder(); xf.MakeMatrixXform()` on the **root prim only** for initial placement. - **Never** add ops to child body/link prims — physics drives those. ## Video Assembly ```bash ffmpeg -y -framerate 30 -i frames/frame_%05d.png \ -c:v libx264 -pix_fmt yuv420p -crf 18 output.mp4 ``` Frame numbering must be **sequential** (`frame_0000.png`, `frame_0001.png`, …) — ffmpeg skips gaps. ## Session Management For batch/iterative rendering, keep the Kit app running and switch stages in-place rather than restarting: - Cold start = 5-7 min wasted - Persistent session = 10-15s per render - Use `stage_utils.open_stage(path)` (`isaacsim.core.experimental.utils.stage`) to switch scenes - Only restart Kit if it crashes or hits OOM Implementation is up to you (REPL, command file, IPC, etc.) — the principle is "don't pay the cold-start cost more than once." ## Checklist Before Delivering Renders 1. RT2 enabled (`/rtx/rendermode = RayTracedLighting`) 2. ACES tone mapping enabled (`/rtx/post/tonemap/op = 4`) 3. filmIso calibrated for scene type (200 general / 400 aerial-heavy / 600 deep-aisle) 4. Explicit `DomeLight + DistantLight` (or scene-specific multi-layer setup) 5. Settle frames sufficient (200 standard / 500 deep-aisle) 6. Frame validation passed (`rgb.mean()` in 30-200 range, file size > 200KB) 7. Frame sequence is gapless for ffmpeg ## Integration Points - **RECEIVES from:** `urdf-mjcf-to-usd-conversion`, `usd-articulation`, `mobility-gen`, `isaac-sim-robot-navigation` — populated stages to render
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기