Scene (Dr.Jit)

rayd.drjit.Scene - building and updating a triangle scene, ray intersection, shadow tests, edge queries, visibility, and topology inspection.

rayd.drjit.Scene holds meshes and the acceleration structures that answer queries over them. This page lists its construction, lifecycle, inspection, ray, edge, and visibility methods. The reflection and diffraction methods are on Reflection and Diffraction.

Every query method has a detached overload (taking Ray, drjit.cuda arrays) and a differentiable one (taking RayAD, drjit.cuda.ad arrays). The result type follows: Intersection or IntersectionAD, and so on. active is a per-lane mask, or True for all lanes.

Scene#

Scene(edge_bvh_backend: str = "auto", trace_backend: str = "auto")
edge_bvh_backendstrdefault 'auto'

Edge query backend: "auto", "optix", "drjit", or "optix_drjit" (point and top-k queries on the Dr.Jit BVH, ray queries on OptiX). "auto" picks OptiX when available.

trace_backendstrdefault 'auto'

Triangle trace backend: "auto", "optix", "cuda", or "none" (no triangle structure, edge queries only). "auto" picks OptiX when the driver accepts an OptiX context, else CUDA.

Raises RuntimeError at build() when an explicitly requested backend is unavailable.

See Scene build and sync.

Building and updating#

add_mesh#

add_mesh(mesh: Mesh, dynamic: bool = False) -> int

Adds a copy of mesh and returns its mesh id (0, 1, 2, ... in call order). A mesh added to a built scene invalidates it until the next build().

meshMeshrequired

The mesh to add.

dynamicbooldefault False

Allow update_mesh_vertices and the transform setters after build(). Static meshes get a faster acceleration structure.

add_instance#

add_instance(geometry_id: int, transform: drjit.cuda.ad.Matrix4f, dynamic: bool = True) -> int

Adds a transformed copy of an existing static mesh that shares its geometry, and returns the new shape id.

geometry_idintrequired

Id of a static mesh added with add_mesh. Dynamic meshes and other instances cannot be instanced.

transformdrjit.cuda.ad.Matrix4frequired

Instance-to-world transform, column vectors.

dynamicbooldefault True

Allow transform updates with set_mesh_transform after build(). Vertices of an instance cannot change.

build#

build() -> None

Builds the triangle acceleration structure and the edge BVH. Required before any query and after adding geometry.

update_mesh_vertices#

update_mesh_vertices(mesh_id: int, positions: drjit.cuda.ad.Array3f) -> None

Queues new object-space positions for a dynamic mesh. The vertex count must stay the same. AD positions make the following queries differentiable with respect to them.

Raises RuntimeError ("target mesh is not dynamic") for a static mesh.

set_mesh_transform#

set_mesh_transform(mesh_id: int, mat: drjit.cuda.ad.Matrix4f, set_left: bool = True) -> None

Queues a replacement of the left (or right) transform factor of a dynamic mesh or instance.

append_mesh_transform#

append_mesh_transform(mesh_id: int, mat: drjit.cuda.ad.Matrix4f, append_left: bool = True) -> None

Queues a multiplication of mat onto the left (or right) transform factor.

set_edge_mask#

set_edge_mask(mask: drjit.cuda.Bool | drjit.cuda.ad.Bool) -> None

Queues an edge mask: one entry per scene-global edge, False hides the edge from edge queries. Ids and offsets do not change. edge_mask() returns the current mask.

sync#

sync() -> None

Applies all queued updates by refitting the affected structures in place. Edge queries raise "scene has pending updates. Call Scene::sync() first." until you call it.

is_ready, has_pending_updates#

is_ready() -> bool
has_pending_updates() -> bool

is_ready() is True after a successful build(). has_pending_updates() is True between a queued update and sync().

Properties#

PropertyMeaning
num_meshesnumber of meshes and instances
versionincreases with every build or geometry sync
edge_versionincreases when the edge set or mask changes
edge_bvh_backendresolved edge backend name
last_sync_profileSceneSyncProfile of the last sync(): stage timings in milliseconds and counts of updated meshes and edges

Inspection#

MethodReturns
trace_backend_name() -> strresolved trace backend: "optix", "cuda", or "none"
capabilities() -> dictresolved backends, integration modes, and per-operation availability
mesh_face_offsets() -> Intprefix sums of face counts per mesh
mesh_edge_offsets() -> Intprefix sums of edge counts per mesh
mesh_vertex_offsets() -> Intprefix sums of vertex counts per mesh
global_geometry() -> SceneGeometryall world-space vertices, faces, face normals, and ids
edge_info() -> SceneEdgeInfogeometry of every global edge
edge_topology() -> SceneEdgeTopologyvertex, face, and opposite-vertex ids of every global edge
edge_bvh_stats() -> SceneEdgeBVHStatsedge BVH size, height, leaf sizes, and overlap (meaningful for the "drjit" edge backend)
edge_mask() -> Boolthe current edge mask

triangle_edge_indices#

triangle_edge_indices(prim_id: drjit.cuda.Int, global_: bool = True) -> tuple

Returns the three edge ids of each triangle as a 3-tuple of Int. With global_=True, both prim_id and the returned ids are scene-global.

edge_adjacent_faces#

edge_adjacent_faces(edge_id: drjit.cuda.Int, global_: bool = True) -> tuple

Returns the two adjacent face ids of each edge as a 2-tuple of Int; -1 marks a missing face.

print(scene.capabilities()["trace_backend"])   # 'optix'
print(scene.mesh_face_offsets())               # [0, 2, 4] for two quads

Ray queries#

intersect#

intersect(ray: Ray, active: Bool = True, flags: RayFlags = RayFlags.All) -> Intersection
intersect(ray: RayAD, active: ad.Bool = True, flags: RayFlags = RayFlags.All) -> IntersectionAD

Finds the closest hit of each ray within [0, ray.tmax].

rayRay | RayADrequired

The rays. A RayAD returns a differentiable result.

activeBooldefault True

Lane mask. Inactive lanes return a miss.

flagsRayFlagsdefault RayFlags.All

Which fields to compute. See RayFlags.

Returns an Intersection. A miss has t = inf and ids of -1.

hit = scene.intersect(rd.Ray(origins, directions))
mask = hit.is_valid()

shadow_test#

shadow_test(ray: Ray | RayAD, active: Bool = True) -> Bool

Returns True for each ray that hits any triangle within [0, ray.tmax]. Cheaper than intersect; it has no gradient.

Edge queries#

nearest_edge#

nearest_edge(point: Array3f, active: Bool = True) -> NearestPointEdge
nearest_edge(ray: Ray, active: Bool = True) -> NearestRayEdge

Finds the closest enabled edge to each point, or to each ray (a segment when tmax is finite). AD inputs return the ...AD record.

pointArray3f | ad.Array3frequired

Query points. Use the ray overload to query by ray instead.

activeBooldefault True

Lane mask.

Returns a NearestPointEdge or NearestRayEdge.

Raises RuntimeError when the scene has pending updates.

nearest_edges#

nearest_edges(point: Array3f, k: int, active: Bool = True) -> NearestEdgesTopK

Finds up to k nearest edges per point, nearest first.

pointArray3f | ad.Array3frequired

Query points.

kintrequired

Edges per point, from 1 to 16.

activeBooldefault True

Lane mask.

Returns a NearestEdgesTopK with query_count * k flat entries; entry q * k + j is the j-th edge of query q.

Visibility#

All visibility queries return masks without gradients. Hits at segment endpoints are not counted.

visible#

visible(start: Array3f, end: Array3f, ignore_prim_ids: Int = [], active: Bool = True) -> SegmentVisibility

Tests whether each segment from start to end is unobstructed.

startArray3frequired

Segment starts.

endArray3frequired

Segment ends.

ignore_prim_idsIntdefault []

Global primitive ids to ignore, K per segment, so the width is a multiple of the segment count. Pad with -1.

activeBooldefault True

Lane mask.

Returns SegmentVisibility with visible and ray_count.

visible_pair#

visible_pair(start: Array3f, end_a: Array3f, end_b: Array3f, ignore_prim_ids: Int = [], active: Bool = True) -> SegmentPairVisibility

Tests two segments that share a start point in one pass. Returns visible_a, visible_b, and ray_count.

visible_edge#

visible_edge(
    src: Array3f,
    edge_pos: Array3f,
    edge_dir: Array3f,
    edge_t_min: Float,
    edge_t_max: Float,
    sample_fractions: Sequence[float] = [0.0, 0.25, 0.5, 0.75, 1.0],
    active: Bool = True,
) -> AxialEdgeVisibility

Tests whether src sees any sample point on the edge segment edge_pos + t * edge_dir, sampled at sample_fractions of [edge_t_min, edge_t_max]. Returns any_visible and state_count.

visible_chain#

visible_chain(points: Array3f, chain_length: Int, ignore_prim_per_segment: Int = [], active: Bool = True) -> SegmentChainVisibility

Tests polylines. Each chain stores up to P points flat, padded to the longest chain.

pointsArray3frequired

Chain points, chain_count * P entries.

chain_lengthIntrequired

Number of points in each chain. A chain of n points has n - 1 segments.

ignore_prim_per_segmentIntdefault []

Primitive ids to ignore per segment; the width is a multiple of chain_count * max_segments.

activeBooldefault True

Chain mask.

Returns SegmentChainVisibility with all_visible, first_blocked_segment, first_blocked_prim (-1 when clear), chain_count, and max_segments.