Back to Work & Blog
3DGSIsaac SimOmniverseWorkflow 2026-07-26~7 min read

Bringing 3DGS into
NVIDIA Isaac Sim

Kou Nakamura here, from Locahun 3D. If you want to put a 3DGS scan to work in robotics or autonomous-mobility simulation, the destination isn't a game engine — it's NVIDIA Isaac Sim. This article lays out the steps to load a scan and render it at photoreal quality, written to hold up for any dataset. The second half covers the fundamental weaknesses of 3DGS in industrial simulation, and how to work around them.

A 3DGS scan rendered in NVIDIA Isaac Sim (pedestrian eye level)
A 3DGS scan rendered in Isaac Sim. 16.5 million Gaussians drawn through RTX. Signage text stays legible, and the real street reproduces as-is.
3DGS scan Decimate the point count Convert to USD Place on stage + fix orientation Place the camera

01 Why Isaac Sim

Where a 3DGS scan should go depends on what you're using it for. For film and video work, UE5 gives you the most freedom. But if the goal is robotics, autonomous mobility, or sensor simulation, NVIDIA Isaac Sim is the obvious choice — because it lets you turn a real location directly into a validation environment.

Isaac Sim 6.0 renders Gaussian splats natively. Because they participate in RTX path tracing, depth of field and motion blur work on them out of the box. That said, bringing real data in surfaces a few walls that aren't documented anywhere.

02 What you'll need

Isaac Sim
6.0 or later. Unpacked, it runs to roughly 40GB, so budget 50GB of free space
Scan data
3DGS .ply
Conversion tool
PlayCanvas splat-transform (npm, free)
USD conversion
Bundled with Isaac Sim by default — no extra install needed

Avoid installing to C:\Program Files. The tool writes data while running, and placing it somewhere that requires administrator privileges invites unnecessary trouble.

Scanner-specific formats can be used without conversionBesides .ply, splat-transform reads the proprietary formats of most major scanners and viewers directly. That saves the step of dropping to an intermediate format — and those packages often ship with a collision mesh and the camera trajectory captured during the scan, both of which pay off later in the pipeline. If you have the original format on hand, starting from it is worth it.

03 First hurdle: exceed the draw-count limit and nothing renders

If you're working with a sizeable scan, get this straight first.

Isaac Sim's RTX renderer can draw at most 224 (16,777,216) points per prim. Go over that, and building the ray-tracing acceleration structure fails — the prim renders zero points, silently. Neither the conversion tool nor the Python API throws an error. You end up with "the conversion succeeded, the point count is correct, and the screen is still black."

What makes this nasty is that every step upstream reports success. The USD file gets the position, scale, opacity, and color of every point written correctly, and once placed on the stage the prim's type is recognized correctly too. Only the rendering silently fails.

This limit isn't documented anywhere official. The only clue is a single line buried in the log: Failed to create TLAS. The textbook fix is "reduce the point count, or split into multiple prims" — this article covers the former.

Check the log before you look at the viewportAfter placing the data on the stage, search the Isaac Sim log for Gaussian prim loaded. If it shows up with a point count, the prim is registered for rendering. If Failed to create TLAS shows up instead, it won't render. Checking this first is the reliable way to avoid misdiagnosing a black screen as a camera problem.

04 Import steps

Bring the point count under the limit

Pass a target point count to splat-transform's --decimate.

# Specify an absolute count (keep it under 2^24 = 16,777,216)
splat-transform input.ply -d 16500000 output.ply

# A percentage also works
splat-transform input.ply -d 95% output.ply

This isn't naive thinning — it's staged pairwise merging of nearby Gaussians. It loses less information than random deletion, and at reduction rates of a few percent the visual degradation is barely noticeable.

Convert to USD and place it on the stage

From Python inside Isaac Sim, use the bundled converter to turn the PLY into USD. Give the output the .usdc extension. Using .usdz stores the contents as text and can more than double the file size.

Before referencing the new data, clean up any existing Gaussian splats on the stage. Leftover old data makes it look like "only part of the new scene is showing," which makes tracking down the actual cause needlessly hard.

from omni.kit.converter.gsplat import convertPlyUSD
convertPlyUSD(r"output.ply", r"scene.usdc")

import omni.usd
stage = omni.usd.get_context().get_stage()

# Remove any existing Gaussian splat prims
for p in list(stage.Traverse()):
    if p.GetTypeName() == "ParticleField3DGaussianSplat":
        stage.RemovePrim(p.GetPath())

# Reference without specifying a type — use OverridePrim
prim = stage.OverridePrim("/World/Scene")
prim.GetReferences().AddReference(r"scene.usdc")
Use OverridePrim, not DefinePrimIf you set an explicit type before adding the reference, the local type declaration overrides the referenced one and the Gaussian splat type is lost. The attributes still come through, but the prim's type changes underneath them and it stops rendering. Create it with OverridePrim, which leaves the type unspecified.

Fix the upside-down orientation

The converter lets you specify the up axis, but converting Z-up scan data with the Y-up setting flips it upside down — buildings end up hanging downward from the ground.

If you miss this and place the camera expecting to be "looking down from above," you're actually looking at the underside of the ground, and the screen comes out black. Applying a −90-degree rotation around the X axis to the prim restores the original orientation.

from pxr import UsdGeom

x = UsdGeom.Xformable(prim)
x.ClearXformOpOrder()
x.AddRotateXOp().Set(-90.0)

05 Placing the camera

The practical trick is not to compute the camera position yourself. Auto-computing a distance from the bounding box doesn't work — it gets dragged around by sparse outliers. Scan data scatters stray points well outside the actual captured area, so the overall extent comes out far larger than the region you actually want to look at.

Instead, use coordinates from the trajectory actually walked during the scan. Most scanners output the movement path as pose data — pick a point on it, place the camera at eye height (around 1.7m), and point it horizontally.

For orientation, use SetLookAt().GetInverse() and feed the result into AddTransformOp(). This form avoids matrix-transpose mistakes.

from pxr import Gf, UsdGeom

eye    = Gf.Vec3d(...)   # A point on the trajectory, at eye height
target = Gf.Vec3d(...)   # The direction to look

xf = Gf.Matrix4d().SetLookAt(eye, target, Gf.Vec3d(0,0,1)).GetInverse()
cx = UsdGeom.Xformable(cam_prim)
cx.ClearXformOpOrder()
cx.AddTransformOp().Set(xf)

The reason to use the trajectory is simple: wherever the person doing the scan actually walked is where the data is recorded at the highest density. Put the camera outside the captured area, and you're looking at surfaces that were never scanned in the first place. If you don't have pose data on hand, the reliable approach is to start close in, do test renders, and pull back while watching what's actually captured.

Camera placement makes the differenceSame data, but the result changes completely depending on where you put the camera.
Outside the scan area 3DGS viewed from outside the scan area — unscanned surfaces break up visually
An unscanned surface. This is the back of a building, and the noise is obvious.
On the trajectory, at eye height View from a camera placed on the scan trajectory — signage text is fully reproduced
A position actually walked. Quality holds up to the point of readable signage.
An overhead view of the intersection rendered in Isaac Sim
The same principle applies to overhead shots. Rather than jumping straight up several hundred meters, pulling back gradually from the trajectory is the reliable approach.
Render latencyPast around 10 million points, RTX convergence takes noticeable time. Before capturing, run await app.next_update_async() for at least 150 frames. Not waiting long enough saves a black image.

06 Weaknesses for industrial simulation

This is where it gets interesting. 3DGS is unbeatable on "how it looks," but it carries almost none of the information industrial simulation actually needs. Here's what turns up when you actually pull outputs from Isaac Sim and check them.

Weakness 1: the whole scene is a single lump — no per-object separation

This is the biggest constraint. A 3DGS scene is one prim holding tens of millions of Gaussians, with no concept of "this building," "that traffic light," or "that car." You can't select object A separately from object B the way you would with 3D models, and you can't move, hide, or swap out a specific object on its own.

Simulation typically assumes operations like "move just the target object" or "swap out an obstacle" — and a 3DGS scan, as scanned, can't do that.

Weakness 2: only one semantic label per scene

Isaac Sim can output semantic segmentation — images classified pixel by pixel as "this is road," "this is a building," and so on — which is central to generating training data for perception AI. Gaussian splats can be assigned labels, and they do show up in the segmentation output.

But the unit a label attaches to is the prim. Since the whole scene is one prim (Weakness 1), the label that ends up applied is also just one, for the entire scene. Pull the actual output and buildings, roads, sidewalks, and traffic lights all come back painted with the same ID. For uses like "train only on the road region" or "extract just the signage," this doesn't work as-is.

Weakness 3: no physical collision

Gaussian splats are appearance data — they carry no surface information. As a result, they can't participate in the physics engine's collision detection. A robot passes straight through walls and falls through the floor. There's a floor to look at, but physically, there's nothing there.

Weakness 4: lighting can't be changed afterward

3DGS bakes the light present at capture time directly into the color data. Scan data captured under an overcast sky stays overcast even if you add sunlight to the scene. If you need training data varied across time of day or weather, this property becomes a real constraint.

07 Working around the weaknesses

Every one of these has a workaround. The shared idea is a division of labor: "3DGS handles appearance, a mesh handles everything else." This is also the architecture NVIDIA itself points toward.

Fix 1: pair it with a collision mesh (physics)

Overlay a simplified proxy mesh at the same position as the 3DGS data, and assign collision to that instead. The splat handles appearance, the mesh handles collision, and the mesh itself stays hidden.

from pxr import UsdPhysics, UsdGeom

mesh = stage.GetPrimAtPath("/World/CollisionMesh")
UsdPhysics.CollisionAPI.Apply(mesh)          # Add collision
UsdGeom.Imageable(mesh).MakeInvisible()      # Hide the appearance

There are three ways to get the mesh. Using a mesh the scanner already produced alongside the scan is the easiest. Failing that, splat-transform can generate a collision mesh directly from the 3DGS data. Or approximate the floor, walls, and obstacles with simple boxes — if the question is just "can the robot get through here," boxes are often enough.

Fix 2: split by region and label each piece (semantics)

If labels attach at the prim level, splitting the prim splits the labels. Cut the splat data up spatially, write each piece out as a separate USD, and assign labels individually.

Do the splitting before bringing the data into Isaac Sim — select regions in SuperSplat (PlayCanvas's free editor) and export them individually, or slice mechanically by coordinates. That said, effort scales with how finely you split. A coarse split like "road / building / everything else" is realistic; manually labeling at the level of individual objects isn't practical.

A more reliable path: put labels on a mesh insteadWhen fine-grained semantics are needed, the practical move is to overlay a labeled mesh as a separate layer. Reuse the collision mesh from Fix 1, split it by part, and assign labels there. Segmentation output comes from the mesh side; appearance comes from the splat side. Even published datasets are structured this way — a splat USD and a collision USD distributed separately and composited at use time.

Fix 3: accept the lighting limitation, or use a different tool

Relighting the 3DGS data itself is outside what Isaac Sim can do. If you need variation across weather or time of day, either run the rendered output back through image-generation AI afterward, or capture the scan multiple times under different conditions. UE5 has plugins that support relighting, so building assets there is another option.

Knowing where it fits

Put all of this together, and the use cases where 3DGS shines and where it doesn't split fairly cleanly.

Use case3DGS aloneNotes
Photoreal backgrounds / visual validationIts biggest strength — reproduces a real location as-is
Generating camera images (as background)Any angle works, but there's no pixel-level ground-truth label
Robot movement / collision testingRequires a collision mesh alongside it
Segmentation training dataRequires a labeled mesh layered on top
Per-object manipulation / swappingOnly possible after splitting; pairing with CG assets is faster
Lighting variationBaked in — needs a separate method

Put simply: 3DGS is a technique for bringing a real environment in as a photoreal background. Whatever moves within it, and whatever needs ground-truth labels, still gets built the traditional way, with meshes. Get that division of labor right, and 3DGS becomes an extremely strong option.

08 Summary

The import path is a straight line: "decimate under 224 points → convert to .usdc → reference with OverridePrim → fix the orientation → place the camera on the trajectory." The trip-ups cluster at the two ends — go over the point limit and nothing renders, with no error, and place the camera outside the scan area and all you see are broken surfaces. Get those two right and the rest goes smoothly.

For industrial use, the fastest way to think about it is that 3DGS is a technique for bringing a real environment in as a photoreal background — nothing more, nothing less. Per-object separation, semantic labels, and collision detection are all things the splat itself doesn't carry. When you need them, layer a mesh on top and split the responsibilities. The flip side is that fidelity of appearance is something no other method really replaces.

Splitting the work between this and UE5 depending on the use case is also realistic. Output aimed at film and video is covered in the UE5 × XGRIDs SDK article. If anything about using 3DGS is on your mind, feel free to reach out.

DATA / 3DGS

The Shibuya scramble crossing 3DGS data used in this article is available to purchase and browse
on Locahun 3D's online platform

Provided as PLY / OBJ (with a 3DGS walkthrough). Standard license from ¥200,000 (commercial and non-commercial use permitted). If you'd like to try this same Isaac Sim workflow yourself, take a look.

View the property data →
Credits

Production credit

Kou Nakamura
Kou Nakamura
Founder, Locahun 3D / Testing & writing

Founder of Locahun 3D (LOCAHUN 3D). Researches and validates capture workflows that combine 3DGS scans with game engines and AI.

Follow @Kou45388803 →