Scene and mesh (PyTorch)
rayd.torch Mesh, Ray, Scene, MultiDeviceOptions, and Camera - construction, updates, ray, edge, visibility, reflection, and diffraction queries on CUDA tensors.
The PyTorch backend takes and returns CUDA tensors. Results are frozen dataclasses with a batch dimension; see Records.
import rayd.torch as rtUnless noted, every tensor must be a contiguous CUDA tensor on the scene's device: positions and directions are float32 with shape (N, 3), indices are int32, and masks are torch.bool with shape (N,). active=None means all lanes. When any continuous input requires a gradient, the query records an autograd node.
Mesh#
Mesh(
vertices: Tensor,
faces: Tensor,
uv: Tensor | None = None,
face_uv: Tensor | None = None,
use_face_normals: bool = False,
edges_enabled: bool = True,
to_world_left: Tensor | None = None,
to_world_right: Tensor | None = None,
)(V, 3) float32 vertex positions. May require a gradient.
(F, 3) int32 vertex indices.
NoneOptional per-vertex texture coordinates.
NoneOptional per-face UV indices.
FalseShade with face normals.
TrueInclude this mesh's edges in edge queries.
NoneOptional (4, 4) float32 transform applied on the left (column vectors).
NoneOptional (4, 4) float32 transform applied on the right.
Ray#
Ray(o: Tensor, d: Tensor, tmax: Tensor | None = None)A frozen batch of rays. o and d are (N, 3) float32; directions need not be normalized. tmax is an optional (N,) tensor; None means unbounded.
RayFlags#
RayFlags.Geometric (1), RayFlags.ShadingN (2), RayFlags.UV (4), and RayFlags.All (7). Same meaning as in Dr.Jit.
Scene#
Scene(
trace_backend: str = "auto",
edge_bvh_backend: str = "auto",
*,
devices: Sequence[int | str | torch.device] | None = None,
options: MultiDeviceOptions | None = None,
)'auto'"auto", "optix", or "cuda".
'auto'"auto", "optix", or "cuda".
NoneReplicate the scene on these CUDA devices and split each batch across them. None uses the device of the input tensors.
NoneTuning for a multi-device scene.
Properties after build(): trace_backend, edge_bvh_backend (resolved names), num_meshes, num_geometries, version, and, for a multi-device scene, device_weights.
Building and updating#
| Method | Description |
|---|---|
add_mesh(mesh, dynamic=False) -> int | adds a mesh, returns its id |
add_instance(geometry_id, transform, dynamic=True) -> int | instance of a static mesh with a (4, 4) transform; not available with devices |
build() -> None | builds acceleration structures |
update_mesh_vertices(mesh_id, positions) -> None | queues new (V, 3) positions for a dynamic mesh |
set_instance_transform(instance_id, transform) -> None | queues a new transform for a dynamic instance |
set_edge_mask(mask) -> None | queues a (E,) bool mask over scene-global edges |
edge_mask() -> Tensor | current edge mask |
sync() -> None | applies queued updates |
has_pending_updates() -> bool, is_ready() -> bool | state |
global_geometry() -> SceneGlobalGeometry | all world-space triangles with ids |
intersect#
intersect(ray: Ray, active: Tensor | None = None, flags: RayFlags = RayFlags.All) -> IntersectionClosest hit per ray. Returns an Intersection with (N,) and (N, 3) tensors; a miss has t = inf and ids of -1.
nearest_edge, nearest_edges#
nearest_edge(point: Tensor | Ray) -> NearestPointEdge | NearestRayEdge
nearest_edges(point: Tensor, k: int, active: Tensor | None = None) -> NearestEdgesTopKClosest edge to each point (N, 3) or ray, and the k closest (at most 16) as (N, k) tensors.
Visibility#
visible(start: Tensor, end: Tensor, active: Tensor | None = None) -> Tensor
visible_pair(start, end_a, end_b, ignore_prim_ids: Tensor | None = None, active=None) -> SegmentPairVisibility
visible_edge(source, edge_position, edge_direction, edge_t_min, edge_t_max,
sample_fractions: Iterable[float] = (0.0, 0.25, 0.5, 0.75, 1.0), active=None) -> AxialEdgeVisibility
visible_chain(points: Tensor, chain_length: Tensor, ignore_prim_per_segment: Tensor | None = None, active=None) -> SegmentChainVisibilityvisible returns a (N,) bool tensor directly. visible_chain takes points as (N, P, 3). Semantics match the Dr.Jit visibility queries.
Reflection#
trace_reflections(ray: Ray, max_bounces: int, active: Tensor | None = None) -> ReflectionChain
trace_refl_epc(source: Tensor, receiver: Tensor, max_bounces: int, options: ReflEpcOptions, active=None) -> ReflEpc
trace_refl_epc_field(source: Tensor, receiver: Tensor, max_bounces: int, active=None) -> ReflEpcField
accumulate_reflections(ray: Ray, tx_position: Tensor, grid: AccumGrid, material: ReflMaterial, max_bounces: int,
options: AccumOptions | None = None, active=None, tx_polarization: Tensor | None = None) -> AccumResulttrace_reflections returns (N, max_bounces) tensors and supports gradients. trace_refl_epc_field and accumulate_reflections are forward-only: they raise RuntimeError when an input requires a gradient or carries a forward-AD tangent.
Diffraction#
accum_dfr_direct(*, states: DfrStates, grid: DfrGrid, material: DfrMaterial | None = None, active=None,
wavelength: float = 1.0, direct_samples: int = 0, keller_samples: int = 0, suffix_samples: int = 0,
seed: int = 0, lane_offset: int = 0, lane_count: int = -1) -> DfrAccum
accum_dfr(initial_states=None, recursive_states=None, grid=None, material=None, active=None, recursive_active=None,
wavelength=1.0, direct_samples=0, keller_samples=0, suffix_samples=0, seed=0, max_order=2,
lane_offset=0, lane_count=-1, **kwargs) -> DfrAccum
accum_dfr_coherent_direct(*, states: DfrStates, grid: DfrGrid, material=None, active=None, wavelength=1.0,
select_diffraction_point=True, prefilter_visibility=True, lane_offset=0, lane_count=-1) -> DfrCoherentAccum
trace_dfr_paths(*, tx_positions: Tensor, rx_positions: Tensor, states: DfrStates, material: DfrMaterial | None = None,
active: Tensor, max_paths: int | None = None, wavelength: float = 1.0, layout: DfrPathLayout | None = None) -> DfrPathsmaterial=None uses default materials. lane_offset and lane_count (-1 = all) select a window of the Monte Carlo sample space; disjoint windows sum to the full result. trace_dfr_paths requires active and is forward-only; layout chooses DfrPathLayout.Compact or SourceLane rows.
acc = scene.accum_dfr_direct(states=states, grid=rt.DfrGrid(resolution0=4, resolution1=4),
material=rt.DfrMaterial.default(1, device="cuda"),
wavelength=1.0, direct_samples=4096, seed=1)Multi-device#
calibrate_devices(*, operation: str | None = None, rays: int = 1 << 20, max_bounces: int = 0,
probe=None, repeats: int = 3, warm_up: int = 1, refine: bool = True) -> DeviceCalibration
device_weights_for(operation: str) -> tuple[float, ...] | Nonecalibrate_devices times a probe (intersect, or trace_reflections when max_bounces is set) on each device and stores the best split for that operation. To time another call, pass both operation and probe(scene, device). Raises RuntimeError on a scene with fewer than two devices.
MultiDeviceOptions#
MultiDeviceOptions(
weights=None, operation_weights=None, require_peer_access=True, require_homogeneous_devices=True,
warm_up=True, chunk_rays=None, offload=None, tape_memory_budget_bytes=None, pipeline=True,
pipeline_chunks_per_device=4, min_rays_per_device=262144, min_lanes_per_device=262144,
)| Field | Meaning |
|---|---|
weights | shard split per device as a ratio; equal by default; 0 leaves a device idle |
operation_weights | split for an operation family ("intersect") or exact shape ("trace_reflections:4") |
require_peer_access | reject devices without bidirectional CUDA peer access |
require_homogeneous_devices | reject a mix of GPU models |
warm_up | pre-link OptiX pipelines on each device during build() |
chunk_rays | rows per launch for the chunked executor |
offload | offload(chunk_start_row, chunk_result) hook; per-ray results are streamed and the call returns None |
tape_memory_budget_bytes | memory budget that sizes the chunks |
pipeline, pipeline_chunks_per_device | overlap scatter, compute, and gather |
min_rays_per_device, min_lanes_per_device | smaller remote shards run on the first device instead |
See Select devices and use several GPUs.
Camera#
Camera(width: int, height: int, fov_x: float)A camera-space pinhole helper at the origin looking along +z. fov_x is the horizontal field of view in degrees, in (0, 180). Property aspect.
| Method | Description |
|---|---|
sample_ray(sample: Tensor) -> Ray | rays through (N, 2) image samples in [0, 1]; (0.5, 0.5) gives d = (0, 0, 1) |
sample_to_world(sample, depth=1.0) -> Tensor | camera-space point at depth |
world_to_sample(point) -> Tensor | image sample of a camera-space point |
The camera has no pose; transform the rays yourself.
Module functions#
backend_capabilities() -> dict returns the flat feature map (including torch_compile and multi_device_replicated). api_manifest() -> dict returns the API and derivative manifest; see Differentiation.