Back to Remotion

3D Flyover — troubleshooting

packages/skills/skills/remotion-maps/techniques/cesium/references/3d-troubleshooting.md

4.0.5066.6 KB
Original Source

3D Flyover — troubleshooting

The headless dead-end (why we render through Remotion)

Cesium's globe will not draw in a standalone headless Playwright/Chromium harness. Verified on Apple M4 (ANGLE Metal active, WebGL working): the skybox/stars render, but the globe surface produces zero draw commands (scene.frameState.commandList.length === 0), globe.tilesLoaded never goes true, and frames come back as the black starfield. No network failures; sampleTerrainMostDetailed succeeds (terrain data is reachable). Dead-ends tried, all failed:

  • default render loop, manual scene.render(), manual viewer.render(), headed mode (context-destroyed).

What works: render Cesium through Remotion — same headless Chrome, but driven by Remotion's frame loop with these four non-negotiables:

  1. useDefaultRenderLoop = false — drive frames by hand.
  2. Per frame call viewer.render(), NOT scene.render(). viewer.render() does the full frame (initializeFrame → tile streaming → render); scene.render() skips frame-init, so tiles never advance and the globe never appears. This is the single most important line.
  3. contextOptions: { webgl: { preserveDrawingBuffer: true } } so Remotion's screenshot captures pixels.
  4. Gate init + every frame with delayRender(…, {timeoutInMilliseconds}) — tile loading can exceed Remotion's default; use 60–120 s.

The standalone flythrough.html / render.mjs / probe.mjs from the original spike are kept only as the record of this dead-end. The canonical render path is the Remotion component (../assets/CesiumFlythrough.tsx).

Symptom → fix

SymptomCauseFix
Frames are black with starsGlobe not drawing (headless Playwright, or scene.render() used)Render through Remotion; use viewer.render(); useDefaultRenderLoop=false.
Screenshots blank/transparentNo preserveDrawingBuffercontextOptions:{ webgl:{ preserveDrawingBuffer:true } }.
"delayRender timed out"Cold tiles exceed the defaultdelayRender(…, {timeoutInMilliseconds: 120000}) + --timeout=180000.
Globe is a dark/navy sphereImagery layer didn't attachbaseLayer:false then viewer.imageryLayers.addImageryProvider(...).
High-pitch frame shows a void/starfield above the horizonNo atmosphereviewer.scene.skyAtmosphere.show = true.
403 on tiles in headlessDomain-locked MapTiler keyUse an unrestricted key.
Google root tileset returns 403Map Tiles API disabled, billing absent, wrong key, or application restriction blocks local headless renderingEnable Map Tiles API and billing; restrict the key to that API while allowing the Remotion request.
Google scene shows a duplicate/competing surfaceMapTiler or the Cesium globe is still enabledDo not add MapTiler; set viewer.scene.globe.show=false.
Google mesh remains coarseScreen-space error is too high or the capture starts before refinementLower maximumScreenSpaceError; settle on tileset.tilesLoaded.
WebGL unavailable / software rendererMissing GL flagRender with --gl=angle.
Camera looks at sky / ground, not terrainPitch sign / conventionCesium pitch 0 = horizon, negative = down (inverse of MapLibre); -(90 - PITCH_FROM_NADIR).
Aim/turn-probe clamps near the endPath too shortPATHKM ≥ TRAVEL_KM + 2·LOOK_AHEAD_KM; raise WINDOW_KM in prep.
Path feels like straight-then-cornerDouglas-Peucker simplificationUse resample → moving-average smooth (see architecture §2), not turf.simplify.
Camera bumps left and right instead of swervingSparse route vertices are still being followed as straight segmentsKeep pathSmoothingPasses={3}; use sparse intentional control points and arc-length movement.

Gotchas checklist

  • viewer.render(), never scene.render() per frame. The single biggest trap.
  • Cesium loads from CDN; set window.CESIUM_BASE_URL before injecting the script.
  • preserveDrawingBuffer: true or screenshots are blank.
  • delayRender uses timeoutInMilliseconds; set it to at least 60,000.
  • Terrain: baseLayer:false, then add MapTiler imagery.
  • Google: no MapTiler, hide the globe, retain showCreditsOnScreen:true.
  • Always skyAtmosphere.show = true.
  • Validate the path's heading-delta probe and render one still (framing + bank) before the full mp4.
  • Cesium pitch/roll conventions are inverted vs MapLibre.