Skip to content

RTT Monitor

App: apps/rtt_monitor_demo/

Demonstrates script render-to-texture capture (engine PLM-242 / ADR 0077). The scene is a top-down ring of six coloured cubes; a small picture-in-picture monitor panel in the top-right shows the same cubes from a second, low-front camera. Each frame the demo renders that second view into a persistent capture texture with Graphics.submitCapture, then samples the texture onto the panel through the ordinary custom-material set-5 path — proving a capture is just a Texture that flows through a normal draw with no descriptor-ABI change.

Terminal window
% ./plume3d rtt_monitor_demo

The main view is a top-down orbit over the ring of cubes; the PiP panel top-right shows the low-front camera’s view of the same ring, updated every frame. The demo captures a screenshot at frame 90.

  • Allocates one persistent capture target in init() (allocate once — never per frame), and a second scene camera (from Scene.addCamera) that supplies the capture’s view/proj but is never made the active view:

    _cap = Graphics.newCaptureTarget(400, 400) // persistent sRGB colour target -> a Texture
    _capNode = _scene.addNode("capcam")
    _capNode.setPosition(0, 4, 13)
    _capCam = _scene.addCamera() // a REAL scene camera — a bare Camera.new() is inert
    _capCam.setNode(_capNode)
    _capCam.setFovYRadians(0.9)
  • Each frame in draw() (not update() — the frame clears pending submissions between the two), binds the view/proj to locals, then submits the scene render into the target:

    var view = _capCam.getViewMatrix()
    var proj = _capCam.getProjectionMatrix(1.0)
    Graphics.submitCapture(_cap, view, proj) // render the whole scene from the second camera into _cap
    _scene.draw()
    // ... draw the ground + ring of cubes ...
  • Binds the capture at set-5 slot 0 on the monitor panel and draws it — a capture is a Texture, so setCustomTexture takes it exactly like a loaded image:

    _screen.setCustomTexture(0, _cap) // the capture, sampled on the PiP panel
    Graphics.drawMesh(_screen)
  • The panel’s set-5 shader (shaders/src/monitor.slang) samples slot 0 with plume3d_customTex(0, uv). Note the MoltenVK gotcha called out in the shader: a set-5 shader must reference enough of the set-5 ABI or slangc strips the unused bindings and the pipeline silently renders nothing.

  • The capture is single-camera in v1 — one submitted capture per frame.
  • submitCapture records the whole scene, including screen-space overlays. Because the monitor panel is itself in the scene it captures, you see the expected live video-feedback recursion; a drawSubset filter to exclude overlays (and to capture a named layer only, for interaction/displacement maps) is a documented follow-up (ADR 0077).
  • Use Graphics.newDataCaptureTarget instead for data maps (grass-bending, water displacement) where you want the linear/UNORM view, not sRGB colour.

See Graphics → Render-to-texture capture (A#7) and the custom material (set 5) sampling path. Screenshot-verified (the PiP panel shows the ring from the second camera).