TAAU

temporal upsampling rebuilds a sharp image from a low-resolution render.

▶ Run in browser

Tags: 3d taa taau render-scale quality post-processing

With render_scale below 1.0 the scene renders at a fraction of the window’s pixels and the tonemap pass upscales it bilinearly, which softens every edge (see render_scale.py). Turn on TAA at the same scale and the upscale becomes TAAU, temporal upsampling:

env = WorldEnvironment(render_scale=0.5, taa_enabled=True)

The TAA resolve then writes at the full window resolution. Each frame renders with a different sub-pixel jitter, so the accumulated history fills in detail between the low-resolution pixels, and after a few frames the image is far sharper than the bilinear upscale for the same number of shaded pixels. At render_scale = 1.0 the same switch is ordinary TAA at native resolution.

The fence of thin pillars is where the difference shows: bilinear upscaling blurs the verticals and TAAU resolves them back to crisp lines. Toggle TAA to compare the two at the same scale. Each button is Button(action=name), so it presses that action’s key and shares the key’s handler; a ButtonGroup keeps one scale button checked.

Controls: T or TAA button - toggle TAA (TAAU or plain bilinear upscale) 1 / 2 / 3 / 4 or buttons - render_scale 0.25 / 0.5 / 0.75 / 1.0

Source

  1# /// simvx
  2# tags = ["3d", "taa", "taau", "render-scale", "quality", "post-processing"]
  3# screenshot_frame = 48
  4# ///
  5"""TAAU: temporal upsampling rebuilds a sharp image from a low-resolution render.
  6
  7With ``render_scale`` below 1.0 the scene renders at a fraction of the window's
  8pixels and the tonemap pass upscales it bilinearly, which softens every edge
  9(see ``render_scale.py``). Turn on TAA at the same scale and the upscale
 10becomes TAAU, temporal upsampling:
 11
 12    env = WorldEnvironment(render_scale=0.5, taa_enabled=True)
 13
 14The TAA resolve then writes at the full window resolution. Each frame renders
 15with a different sub-pixel jitter, so the accumulated history fills in detail
 16between the low-resolution pixels, and after a few frames the image is far
 17sharper than the bilinear upscale for the same number of shaded pixels. At
 18``render_scale = 1.0`` the same switch is ordinary TAA at native resolution.
 19
 20The fence of thin pillars is where the difference shows: bilinear upscaling
 21blurs the verticals and TAAU resolves them back to crisp lines. Toggle TAA to
 22compare the two at the same scale. Each button is ``Button(action=name)``, so
 23it presses that action's key and shares the key's handler; a ``ButtonGroup``
 24keeps one scale button checked.
 25
 26Controls:
 27    T or TAA button          - toggle TAA (TAAU or plain bilinear upscale)
 28    1 / 2 / 3 / 4 or buttons - render_scale 0.25 / 0.5 / 0.75 / 1.0
 29"""
 30
 31from simvx.core import (
 32    Anchor,
 33    Button,
 34    ButtonGroup,
 35    Camera3D,
 36    CanvasLayer,
 37    DirectionalLight3D,
 38    HBoxContainer,
 39    Key,
 40    Label,
 41    Material,
 42    Mesh,
 43    MeshInstance3D,
 44    Node,
 45    WorldEnvironment,
 46    on_input,
 47)
 48from simvx.graphics import App
 49
 50SCALES = {"scale_25": 0.25, "scale_50": 0.5, "scale_75": 0.75, "scale_100": 1.0}
 51
 52
 53class TAAU(Node):
 54    input_context = {
 55        "taa": [Key.T],
 56        "scale_25": [Key.KEY_1],
 57        "scale_50": [Key.KEY_2],
 58        "scale_75": [Key.KEY_3],
 59        "scale_100": [Key.KEY_4],
 60    }
 61
 62    def on_ready(self):
 63        self.env = self.add_child(WorldEnvironment(render_scale=0.5, taa_enabled=True))
 64        self.add_child(Camera3D(position=(0, 3.2, 9.0), look_at=(0, 1.2, 0)))
 65        self.add_child(DirectionalLight3D(intensity=2.2, direction=(-0.5, -1.0, -0.4)))
 66
 67        ground = Material(colour=(0.55, 0.55, 0.6), roughness=0.9)
 68        self.add_child(MeshInstance3D(mesh=Mesh.box((16, 0.5, 16)), material=ground, position=(0, -0.25, 0)))
 69        pillar, brick = Mesh.box((0.18, 2.7, 0.18)), Material(colour=(0.85, 0.35, 0.2), roughness=0.5)
 70        for i in range(-5, 6):
 71            self.add_child(MeshInstance3D(mesh=pillar, material=brick, position=(i * 1.1, 1.4, -3.0)))
 72        blue = Material(colour=(0.3, 0.7, 1.0), roughness=0.3)
 73        self.add_child(MeshInstance3D(mesh=Mesh.sphere(0.8), material=blue, position=(0, 1.3, 0.5)))
 74        green = Material(colour=(0.35, 0.75, 0.4), roughness=0.35)
 75        self.add_child(MeshInstance3D(mesh=Mesh.box(1.1), material=green, position=(2.8, 0.55, 1.6)))
 76
 77        hud = self.add_child(CanvasLayer())
 78        self.status = hud.add_child(Label(anchor=Anchor.TOP_LEFT, inset=16, font_size=28))
 79        bar = hud.add_child(HBoxContainer(anchor=Anchor.BOTTOM_LEFT, inset=16))
 80        self.taa_button = bar.add_child(Button("TAA", action="taa", checked=True))
 81        group = ButtonGroup()
 82        self.buttons = {name: bar.add_child(Button(str(s), action=name, group=group)) for name, s in SCALES.items()}
 83        self.buttons["scale_50"].checked = True
 84        self.show_mode()
 85
 86    @on_input("taa")
 87    def toggle_taa(self, _event):
 88        self.env.taa_enabled = not self.env.taa_enabled
 89        self.show_mode()
 90
 91    @on_input(tuple(SCALES))
 92    def set_scale(self, event):
 93        self.env.render_scale = SCALES[event.action]
 94        self.buttons[event.action].checked = True
 95        self.show_mode()
 96
 97    def show_mode(self):
 98        taa, scale = self.env.taa_enabled, self.env.render_scale
 99        if scale == 1.0:
100            mode = "TAA (native)" if taa else "native"
101        else:
102            mode = "TAAU (temporal upsample)" if taa else "bilinear upscale"
103        self.taa_button.checked = taa
104        self.status.text = f"render_scale = {scale}  |  {mode}"
105
106
107if __name__ == "__main__":
108    App(title="TAAU Temporal Upsampling", width=1280, height=720).run(TAAU())