Time-gated rendering (offline)

This tutorial renders a time-gated image of the Cornell box offline with TimeGatedPathTracerInline: only light whose total path length (laser -> scene -> camera) falls inside a gate reaches the image. The scene is the Cornell box without its area light, so a laser is the only light: download scene-v4-nolight.pbrt and save it as cornell-box/scene-v4-nolight.pbrt next to the script. The other tutorials use the same file. (The Cornell box is by Benedikt Bitterli, released under CC0.)

Time-gated Cornell box

1. Load the scene

import falcorcomp as falcor

testbed = falcor.Testbed(create_window=False)
testbed.load_scene("cornell-box/scene-v4-nolight.pbrt")
testbed.resize_frame_buffer(512, 512)
testbed.scene.camera.aspectRatio = 1.0
testbed.clock.pause()

2. Build the render graph

VBufferRT finds the primary hits and LaserLight places the laser; the execution edge add_edge("Laser", "Tracer") (pass names only) makes it run before the tracer. The tracer keeps paths whose length lies in a box gate of width timeGateWindow centered at timeMin (equal to timeMax for a single gate). AccumulatePass averages the frames and ToneMapper makes a displayable copy.

graph = testbed.create_render_graph("TimeGated")
graph.create_pass("VBuffer", "VBufferRT", {"samplePattern": "Center", "sampleCount": 1})
graph.create_pass("Laser", "LaserLight", {
    "laserPosition": [0.0, 1.7, 6.8], "laserDirection": [0.0, 0.0, -1.0],
    "laserPower": [170.0, 120.0, 40.0], "laserAngle": 0.0,
})
graph.create_pass("Tracer", "TimeGatedPathTracerInline", {
    "samplesPerPixel": 16, "maxBounces": 6, "computeDirect": False,
    "timeGateMode": "box", "timeGateWindow": 0.1, "timeMin": 17.337, "timeMax": 17.337,
})
graph.create_pass("Accumulate", "AccumulatePass", {"precisionMode": "SingleCompensated"})
graph.create_pass("ToneMapper", "ToneMapper", {"autoExposure": False})

graph.add_edge("VBuffer.vbuffer", "Tracer.vbuffer")
graph.add_edge("VBuffer.viewW", "Tracer.viewW")
graph.add_edge("Laser", "Tracer")
graph.add_edge("Tracer.color", "Accumulate.input")
graph.add_edge("Accumulate.output", "ToneMapper.src")
graph.mark_output("Accumulate.output")  # output 0: linear radiance
graph.mark_output("ToneMapper.dst")     # output 1: tone mapped
testbed.render_graph = graph

3. Render

Each frame traces samplesPerPixel paths per pixel and adds them to the accumulated image.

for _ in range(256):  # 256 frames x 16 spp = 4096 spp
    testbed.frame()

4. Save the image

Output 0 is the linear radiance (EXR); output 1 is the tone-mapped image (PNG).

testbed.capture_output("time_gated.exr", 0)
testbed.capture_output("time_gated.png", 1)

The full script: time_gated_offline.py.