RenderView

the main scene from a second camera, shown on an in-world monitor.

▶ Run in browser

Tags: 3d renderview render-to-texture camera

A RenderView renders the main scene through a second Camera3D into an offscreen texture, every frame. Here that camera is a security camera hung above the set, and its feed plays on a monitor standing in the same world.

  • RenderView(camera="SecurityCam", size=(512, 512)) names its camera by node path, resolved relative to the view; here the camera is the view’s child.

  • The security camera is built with visible=False. The scene renders through the first visible Camera3D, so a hidden camera never takes over the main view, and the RenderView still renders through it.

  • Material(albedo_map=view, unlit=True) puts the live feed on a surface. The material follows the view’s texture every frame, so there is nothing to copy or refresh by hand.

A SubViewport renders its own subtree; a RenderView has no subtree of its own and re-renders the one main world from another viewpoint. The orange cube spins in both views at once, so the monitor is a live capture, not a snapshot.

Keep the monitor out of the security camera’s view: a camera that sees the surface showing its own feed would sample the texture it is rendering into.

Source

  1# /// simvx
  2# tags = ["3d", "renderview", "render-to-texture", "camera"]
  3# screenshot_frame = 30
  4# ///
  5"""RenderView: the main scene from a second camera, shown on an in-world monitor.
  6
  7A `RenderView` renders the main scene through a second `Camera3D` into an
  8offscreen texture, every frame. Here that camera is a security camera hung
  9above the set, and its feed plays on a monitor standing in the same world.
 10
 11- `RenderView(camera="SecurityCam", size=(512, 512))` names its camera by node
 12  path, resolved relative to the view; here the camera is the view's child.
 13- The security camera is built with `visible=False`. The scene renders through
 14  the first visible `Camera3D`, so a hidden camera never takes over the main
 15  view, and the `RenderView` still renders through it.
 16- `Material(albedo_map=view, unlit=True)` puts the live feed on a surface. The
 17  material follows the view's texture every frame, so there is nothing to
 18  copy or refresh by hand.
 19
 20A `SubViewport` renders its own subtree; a `RenderView` has no subtree of its
 21own and re-renders the one main world from another viewpoint. The orange cube
 22spins in both views at once, so the monitor is a live capture, not a snapshot.
 23
 24Keep the monitor out of the security camera's view: a camera that sees the
 25surface showing its own feed would sample the texture it is rendering into.
 26"""
 27
 28from simvx.core import (
 29    Anchor,
 30    Camera3D,
 31    CanvasLayer,
 32    DirectionalLight3D,
 33    Label,
 34    Material,
 35    Mesh,
 36    MeshInstance3D,
 37    Node,
 38    Quat,
 39    RenderView,
 40)
 41from simvx.graphics import App
 42
 43
 44class RenderViewScene(Node):
 45    def on_ready(self):
 46        self.add_child(Camera3D(position=(0, 2.2, 7.0), look_at=(0, 0.4, 0)))
 47        self.add_child(DirectionalLight3D(intensity=2.5, direction=(-0.4, -1.0, -0.6)))
 48
 49        # Aimed at the set's centre-right, so the monitor (front-left) stays out of frame.
 50        view = self.add_child(RenderView(camera="SecurityCam", size=(512, 512)))
 51        view.add_child(
 52            Camera3D(name="SecurityCam", visible=False, fov=45.0, position=(4.5, 5.5, 4.5), look_at=(1.2, 0.3, 0.3))
 53        )
 54
 55        self.add_child(
 56            MeshInstance3D(
 57                mesh=Mesh.box((12.0, 0.5, 12.0)),
 58                material=Material(colour=(0.35, 0.38, 0.42, 1.0), roughness=0.9),
 59                position=(0, -0.75, 0),
 60            )
 61        )
 62        self.spinner = self.add_child(
 63            MeshInstance3D(
 64                mesh=Mesh.box(1.2),
 65                material=Material(colour=(0.95, 0.55, 0.15, 1.0), roughness=0.4),
 66                position=(0, 0.4, 0),
 67            )
 68        )
 69        self.add_child(
 70            MeshInstance3D(
 71                mesh=Mesh.sphere(0.5),
 72                material=Material(colour=(0.3, 0.5, 0.95, 1.0), roughness=0.3),
 73                position=(2.2, 0.0, 1.2),
 74            )
 75        )
 76        self.add_child(
 77            MeshInstance3D(
 78                mesh=Mesh.box((0.6, 1.8, 0.6)),
 79                material=Material(colour=(0.3, 0.8, 0.4, 1.0)),
 80                position=(-2.2, 0.4, -1.0),
 81            )
 82        )
 83
 84        # The monitor, angled toward the main camera.
 85        self.add_child(
 86            MeshInstance3D(
 87                mesh=Mesh.box((2.0, 2.0, 0.08)),
 88                material=Material(albedo_map=view, unlit=True),
 89                position=(-2.6, 1.5, 3.4),
 90                rotation=Quat.from_euler(0.0, 0.5, 0.0),
 91            )
 92        )
 93
 94        hud = self.add_child(CanvasLayer())
 95        hud.add_child(Label("RenderView: overhead security feed", anchor=Anchor.TOP_LEFT, inset=16, font_size=24))
 96
 97    def on_update(self, dt):
 98        self.spinner.rotate_y(1.4 * dt)
 99        self.spinner.rotate_x(0.7 * dt)
100
101
102if __name__ == "__main__":
103    App(width=1280, height=720, title="SimVX - RenderView").run(RenderViewScene())