Python: Sliced Rendering#

Renders one 1024×1024 RenderProduct as four successive 512×512 dataWindowNDC crops using PathTracing at 64 samples per pixel. The example uses zero client warmup frames, waits for each crop to converge, and stitches the LdrColor outputs from the TL, TR, BL, and BR tiles into one RGBA image.

No quality attribute is authored; streaming wait uses the renderer defaults.

“Render one image as four successive dataWindowNDC crops without client warmup frames, and stitch the captured TL, TR, BL, and BR tiles into a full-resolution image.”

Full image stitched from four rendered crops

The tested crop-update and stitching pattern is:

def capture(ordinal):
    """Wait for the current crop to converge before copying its pixels."""
    # A crop change clears accumulation; capture only the completed image.
    while True:
        products = renderer.step(
            render_products={"/Render/Camera"},
            delta_time=1.0 / 60,
            ordinal=ordinal,
        )
        frame = products["/Render/Camera"].frames[-1]
        if frame.converged:
            break

    var = frame.render_vars[LDR_COLOR_PATH].map(device=ovrtx.Device.CPU)
    view = np.from_dlpack(var)
    pixels = view.copy()
    del view
    var.unmap()
    del var, frame, products
    return pixels

full_frame = capture(ordinal=1)
assert full_frame.shape == (512, 512, 4)
assert full_frame.dtype == np.uint8

slices = (
    # LdrColor rows follow dataWindowNDC's bottom-up Y order.
    ("TL", (0.0, 0.5, 0.5, 1.0), (256, 0)),
    ("TR", (0.5, 0.5, 1.0, 1.0), (256, 256)),
    ("BL", (0.0, 0.0, 0.5, 0.5), (0, 0)),
    ("BR", (0.5, 0.0, 1.0, 0.5), (0, 256)),
)
stitched = np.empty((512, 512, 4), dtype=np.uint8)

with ovstage.PathDictionary(stage) as paths:
    path_list = paths.create_path_list_from_strings(["/Render/Camera"])
    try:
        with stage.query_from_path_list(path_list) as query:
            attribute = paths.intern_token("dataWindowNDC")
            crop_dtype = ovstage.numpy_to_dldatatype(np.dtype(np.float32), lanes=4)

            for ordinal, (label, crop_ndc, (dst_y, dst_x)) in enumerate(slices, start=2):
                crop_values = np.asarray([crop_ndc], dtype=np.float32)
                crop_tensor = ovstage.make_dltensor(
                    crop_values,
                    dtype=crop_dtype,
                    shape=[1],
                    ndim=1,
                )
                stage.write_attribute(
                    query,
                    attribute,
                    ordinal=ordinal,
                    tensors=crop_tensor,
                    is_array=False,
                ).wait()
                stage.advance_write_floor(ordinal, ovstage.Scope.ALL).wait()

                pixels = capture(ordinal)
                assert pixels.shape == (256, 256, 4)
                assert pixels.dtype == np.uint8
                reference_tile = full_frame[dst_y : dst_y + 256, dst_x : dst_x + 256, :3]
                tile_error = np.abs(
                    pixels[:, :, :3].astype(np.int16) - reference_tile.astype(np.int16)
                ).mean()
                assert tile_error < 8.0, f"{label} tile differs from its full-frame region: MAE={tile_error}"
                stitched[dst_y : dst_y + 256, dst_x : dst_x + 256] = pixels
    finally:
        paths.destroy_path_list(path_list)

Image.fromarray(full_frame).save(output_dir / "base.Camera.LdrColor.full.png")
Image.fromarray(stitched).save(output_dir / "base.Camera.LdrColor.sliced.png")

Prerequisites#

  • Python 3.10-3.13

  • uv

  • An NVIDIA RTX-capable GPU and a supported driver

Running#

cd examples/python/sliced-rendering
uv run main.py

Pass --png to save _output/render.png instead of displaying the image.

Note

This example uses Path Tracing. When using this technique with Real-Time Path Tracing, changing dataWindowNDC clears temporal history. Render additional warm-up frames for each crop after the first before using its result.