HyperLux: Simple Batch

Render 1 HSSD scene × 1,024 cameras at 512×512. The first run downloads scenes and compiles native code.

With the OptiX backend, camera inputs and rendered frames stay resident on the GPU.

%pip install -q hyperlux torch matplotlib
import torch
import hyperlux
from hyperlux.camera import RGB

BATCH_SIZE, SIZE = 1024, 512
config = hyperlux.Config(width=SIZE, height=SIZE, batch_size=BATCH_SIZE)
dataset = hyperlux.datasets.procthor.Train200()
renderer = hyperlux.HyperLux(config=config)
scene = hyperlux.load_scene(dataset[0].reference)
renderer.init(scene)

device = torch.device("cuda" if renderer.backend == "optix" else "cpu")
stream = torch.cuda.current_stream(device).cuda_stream if device.type == "cuda" else 0
#rl_tools::hyperlux: backend=optix
#rl_tools::hyperlux::datasets: Loaded model with 95 mesh(es)
#rl_tools::hyperlux::datasets: Scene file has no lights: adding 3 directional fill lights

Supply camera poses as float32 (batch, 4, 3) ray bases: position, top-left ray, horizontal increment, vertical increment, in world coordinates (+X forward, +Y left, +Z up).

yaw = torch.arange(BATCH_SIZE, device=device) * (2 * torch.pi / BATCH_SIZE)
forward = torch.stack((yaw.cos(), yaw.sin(), torch.zeros_like(yaw)), dim=1)
right = torch.stack((yaw.sin(), -yaw.cos(), torch.zeros_like(yaw)), dim=1)
up = torch.tensor([0., 0., 1.], device=device).expand_as(forward)
positions = torch.as_tensor(hyperlux.FreeSpace(scene).sample(BATCH_SIZE, seed=0), device=device)
camera_bases = torch.stack((positions, forward - right + up, 2 * right, -2 * up), dim=1)
#rl_tools::hyperlux: backend=optix

Render RGBA frames into a (cameras, height, width, 4) tensor. Rerun this cell after updating camera_bases.

frames = torch.empty((BATCH_SIZE, SIZE, SIZE, 4), dtype=torch.uint8, device=device)
renderer.set_cameras(camera_bases, stream=stream)
renderer.render()
renderer.outputs[RGB()].read(out=frames, stream=stream)
print(f"{frames.shape}: {frames.dtype} on {frames.device}")
torch.Size([1024, 512, 512, 4]): torch.uint8 on cuda:0

Preview 64 views in four rows of sixteen. Only these previews are copied to the CPU.

import matplotlib.pyplot as plt
from matplotlib_inline.backend_inline import set_matplotlib_formats
set_matplotlib_formats("jpeg", bbox_inches=None, pil_kwargs={"quality": 80, "optimize": True})

preview = frames[::BATCH_SIZE // 64, :, :, :3].cpu()
fig, axes = plt.subplots(4, 16, figsize=(16, 4), dpi=100, squeeze=False)
for ax, image in zip(axes.flat, preview):
    ax.imshow(image)
    ax.axis("off")
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, wspace=0.02, hspace=0.02)
../_images/355be581e1e0d96bdcd2761e5c2f1323a9a6b46f482fe0cbfaf9c636f4828c14.jpg