Summary
A Gaussian splat tileset renders as a pure black frame (every pixel 0,0,0) through headless Movie Render Queue, because MRQ captures the still before the splat tiles have been uploaded to the GPU. updateViewGroupOffline() — the "wait for tiles" offline path — does not appear to hold the MRQ capture until the Niagara splat buffers are ready, or is not engaged for a CLI -game launch.
The failure is silent and easy to misattribute: the log says the tileset loaded to 100 %, reports tiles "Rendered", compiles the Niagara system, and shows no errors. There is a straightforward workaround (below), so this is more a request to make the offline path actually block, or to document that it does not.
Why it is easy to miss
The PlayCanvas dragon sample renders fine through the identical script, which sends you looking for a difference between the two assets rather than at timing. The difference is size, not content:
| Tileset |
Tiles |
Size |
Result |
| PlayCanvas dragon sample |
3 |
1.6 MB |
renders correctly |
| Local building capture |
16 |
32 MB |
pure black |
The dragon's coarse root tile gets loaded and uploaded inside the warm-up window; the larger tileset does not.
Evidence
With MRQ's default 1 warm-up frame, Finished processing Camera Cut landed at Unreal frame 6 in every run. Cesium's own per-frame stats for the 16-tile tileset at that moment:
- frame 3: 4.5 % loaded
- frame 4: 27 % loaded
- frame 5: 71 % loaded
- 100 % only at frames 8–20
So the capture happens at frame 6, while the tileset is still streaming.
Ruled out along the way, in case it saves anyone time:
- Stripping spherical harmonics (
--sh 0) — still black
- The
COLOR_0 accessor type — still black
- Camera framing at 25 m / 70 m / 200 m — still black at all three
- Content occluding the camera — ruled out because the good dragon frame also has no floor and no sky (the test level's ground plane and
SkyAtmosphere do not render in this -game MRQ world at all). So max pixel = 0 meant nothing was drawn, not something was in the way.
- Oversized splats in the source PLY — the model does contain 20 splats with σ ≥ 10 m, and removing everything with σ ≥ 1 m did not change the black frame. (They are a real quality problem, just not this one.)
Workaround
Giving MRQ a generous warm-up fixes it completely:
aa = ... # MoviePipelineAntiAliasingSetting
aa.set_editor_property("engine_warm_up_count", 120)
aa.set_editor_property("render_warm_up_count", 30)
aa.set_editor_property("render_warm_up_frames", True)
120 engine ticks is empirical and almost certainly more than needed for this tileset; I have not tried to find the minimum, because the correct number presumably scales with tileset size and network conditions, which is rather the point.
What would be better
Either of these would remove the guesswork:
- Have
updateViewGroupOffline() (or whatever the _captureMovieMode path uses) actually block the capture until the splat buffers are uploaded, not merely until tiles are loaded — the gap between "loaded" and "on the GPU as Niagara buffers" seems to be where this falls through.
- Failing that, document that a splat tileset requires a warm-up count under MRQ, since the symptom (a silent black frame with a clean log) gives no hint at all as to the cause.
I would also gently suggest that "loaded 100 %" being reported while the render is still black is itself misleading, whatever the fix.
Environment
- Cesium for Unreal v2.29.0, prebuilt Linux binaries
- Unreal Engine 5.8.1, prebuilt Linux editor, Vulkan
- Linux x86_64 (Fedora, kernel 7.1.8), NVIDIA GTX 1660 6 GB
- Headless
-game launch driving MRQ from a Python preset (no editor session)
- Tileset built locally with
splat-transform 3.3.0 → 3dgs-ply-3dtiles-converter 0.6.2 (3D Tiles 1.1, KHR_gaussian_splatting + _compression_spz_2), served over file:///, no Cesium ion
- Source splat trained in Brush, 10k steps, ~501k splats
Happy to share the tileset and the full render logs.
Summary
A Gaussian splat tileset renders as a pure black frame (every pixel
0,0,0) through headless Movie Render Queue, because MRQ captures the still before the splat tiles have been uploaded to the GPU.updateViewGroupOffline()— the "wait for tiles" offline path — does not appear to hold the MRQ capture until the Niagara splat buffers are ready, or is not engaged for a CLI-gamelaunch.The failure is silent and easy to misattribute: the log says the tileset loaded to 100 %, reports tiles "Rendered", compiles the Niagara system, and shows no errors. There is a straightforward workaround (below), so this is more a request to make the offline path actually block, or to document that it does not.
Why it is easy to miss
The PlayCanvas dragon sample renders fine through the identical script, which sends you looking for a difference between the two assets rather than at timing. The difference is size, not content:
The dragon's coarse root tile gets loaded and uploaded inside the warm-up window; the larger tileset does not.
Evidence
With MRQ's default 1 warm-up frame,
Finished processing Camera Cutlanded at Unreal frame 6 in every run. Cesium's own per-frame stats for the 16-tile tileset at that moment:So the capture happens at frame 6, while the tileset is still streaming.
Ruled out along the way, in case it saves anyone time:
--sh 0) — still blackCOLOR_0accessor type — still blackSkyAtmospheredo not render in this-gameMRQ world at all). Somax pixel = 0meant nothing was drawn, not something was in the way.Workaround
Giving MRQ a generous warm-up fixes it completely:
120 engine ticks is empirical and almost certainly more than needed for this tileset; I have not tried to find the minimum, because the correct number presumably scales with tileset size and network conditions, which is rather the point.
What would be better
Either of these would remove the guesswork:
updateViewGroupOffline()(or whatever the_captureMovieModepath uses) actually block the capture until the splat buffers are uploaded, not merely until tiles are loaded — the gap between "loaded" and "on the GPU as Niagara buffers" seems to be where this falls through.I would also gently suggest that "loaded 100 %" being reported while the render is still black is itself misleading, whatever the fix.
Environment
-gamelaunch driving MRQ from a Python preset (no editor session)splat-transform3.3.0 →3dgs-ply-3dtiles-converter0.6.2 (3D Tiles 1.1,KHR_gaussian_splatting+_compression_spz_2), served overfile:///, no Cesium ionHappy to share the tileset and the full render logs.