Skip to main content

RayD: minimalist differentiable ray tracing package wrapping Dr.Jit and OptiX.

Project description

RayD

PyPI Downloads Code Size Total Lines License

RayD is a minimalist differentiable ray tracing package wrapping OptiX ray tracing with Dr.Jit autodiff.

pip install rayd

RayD is not a full renderer. It is a thin wrapper around Dr.Jit and OptiX for building your own renderers and simulators.

The goal is simple: expose differentiable ray-mesh intersection on the GPU without bringing in a full graphics framework.

RayD provides three frontends:

  • Dr.Jit (Native) — direct Dr.Jit array API, maximum control
  • PyTorchrayd.torch module, CUDA torch.Tensor in/out, integrates with torch.autograd
  • Slang — C++ POD/handle bridge for Slang cpp target interop

Why RayD?

RayD is for users who want OptiX acceleration and autodiff, but do not want a full renderer.

Why not Mitsuba? Mitsuba is excellent for graphics rendering, but often too high-level for RF, acoustics, sonar, or custom wave simulation. In those settings, direct access to ray-scene queries and geometry gradients is usually more useful than a full material-light-integrator stack.

RayD keeps only the geometric core:

  • differentiable ray-mesh intersection
  • scene-level GPU acceleration through OptiX
  • edge acceleration structures for nearest-edge queries
  • primary-edge sampling support for edge-based gradient terms

For intersection workloads, RayD targets Mitsuba-level performance and matching results with a much smaller API surface.

What RayD Provides

  • Mesh: triangle geometry, transforms, UVs, and edge topology
  • Scene: a container of meshes plus OptiX acceleration
  • scene.intersect(ray): differentiable ray-mesh intersection
  • scene.shadow_test(ray): occlusion testing
  • scene.nearest_edge(query): nearest-edge queries for points and rays, returning shape_id, mesh-local edge_id, and scene-global global_edge_id
  • scene.set_edge_mask(mask) / scene.edge_mask(): scene-global filtering for the secondary-edge BVH used by nearest_edge(...)
  • edge acceleration data that is useful for edge sampling and edge diffraction methods

Performance

The chart below was generated on March 25, 2026 on an NVIDIA GeForce RTX 5080 and AMD Ryzen 7 9800X3D, comparing RayD (0.1.2) against Mitsuba 3.8.0 with the cuda_ad_rgb variant.

Raw benchmark data is stored in docs/performance_benchmark.json.

  • RayD is consistently faster on static forward and static gradient workloads across all three scene sizes.
  • Dynamic reduced forward reaches parity or better from the medium scene onward, and dynamic full is effectively tied on the largest case.
  • On the largest 192x192 mesh / 384x384 ray benchmark, RayD vs Mitsuba average latency in milliseconds is: static full 0.162 vs 0.190, static reduced 0.124 vs 0.224, dynamic full 0.741 vs 0.740, dynamic reduced 0.689 vs 0.714, gradient static 0.411 vs 0.757, gradient dynamic 1.324 vs 1.413.
  • Correctness stayed aligned throughout the sweep: forward mismatch counts remained 0, and the largest static gradient discrepancy was 9.54e-7.

RayD vs Mitsuba performance benchmark

Quick Examples

If you only want to see the package in action, start here:

Differentiable Cornell Box with Edge Sampling

GPU path tracing + interior AD + edge sampling (Li et al.) in ~180 lines (examples/renderer/cornell_box.py):

Differentiable Cornell box render and edge-AD gradient

Differentiable Radio Frequnecy Wave Propagation

(Mini-Differentiable-RF-Digital-Twin):

Differentiable Radio Frequnecy Wave Propagation

Minimal Differentiable Ray Tracing Example

The example below traces a single ray against one triangle and backpropagates the hit distance to the vertex positions.

import rayd as rd
import drjit as dr


mesh = rd.Mesh(
    dr.cuda.Array3f([0.0, 1.0, 0.0],
                    [0.0, 0.0, 1.0],
                    [0.0, 0.0, 0.0]),
    dr.cuda.Array3i([0], [1], [2]),
)

verts = dr.cuda.ad.Array3f(
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0],
    [0.0, 0.0, 0.0],
)
dr.enable_grad(verts)

mesh.vertex_positions = verts

scene = rd.Scene()
scene.add_mesh(mesh)
scene.build()

ray = rd.Ray(
    dr.cuda.ad.Array3f([0.25], [0.25], [-1.0]),
    dr.cuda.ad.Array3f([0.0], [0.0], [1.0]),
)

its = scene.intersect(ray)
loss = dr.sum(its.t)
dr.backward(loss)

print("t =", its.t)
print("grad z =", dr.grad(verts)[2])

This is the core RayD workflow. Replace the single ray with your own batched rays, RF paths, acoustic paths, or edge-based objectives.

PyTorch Frontend

rayd.torch is an optional Python-level wrapper that mirrors the native API using CUDA torch.Tensor inputs and outputs. AD mode is inferred automatically from requires_grad.

import rayd.torch as rt

verts = torch.tensor([...], device="cuda", requires_grad=True)
mesh = rt.Mesh(verts, faces)
scene = rt.Scene()
scene.add_mesh(mesh)
scene.build()

its = scene.intersect(rt.Ray(origins, directions))
loss = (its.t - target).pow(2).mean()
loss.backward()  # gradients flow to verts

Key conventions:

  • vectors use shape (N, 3) or (N, 2); (3,) and (2,) are accepted as batch size 1
  • index tensors use shape (F, 3); images use shape (H, W); transforms use shape (4, 4)
  • CPU tensors are rejected; rayd.torch does not do implicit device transfers

The native Dr.Jit API remains unchanged and does not depend on PyTorch.

Device Selection

RayD follows Dr.Jit's current-thread CUDA device selection. If you need to choose a GPU explicitly, do it before constructing any RayD resources:

import rayd as rd

rd.set_device(0)  # also initializes OptiX on that device by default

rd.set_device() / rayd.torch.set_device() are intended for selecting the device up front. Existing RayD scenes, OptiX pipelines, and BVHs should not be reused across device switches in the same process.

Slang Frontend

RayD ships a Slang interop layer for Slang's cpp target. Slang code can import rayd_slang; and call RayD scene queries directly.

NearestPointEdge and NearestRayEdge returned through Slang include global_edge_id in the same scene-global index space as scene.edge_info().global_edge_id. The Slang bridge also exposes scene edge-mask helpers for host code: sceneEdgeCount(scene), sceneEdgeMaskValue(scene, index), and sceneSetEdgeMask(scene, maskPtr, count).

Minimal Slang Example

import rayd_slang;

export float traceRayT(uint64_t sceneHandle,
                       float ox, float oy, float oz,
                       float dx, float dy, float dz)
{
    SceneHandle scene = makeSceneHandle(sceneHandle);
    Ray ray = makeRay(float3(ox, oy, oz), float3(dx, dy, dz));
    Intersection hit = sceneIntersect(scene, ray);
    return itsT(hit);  // use accessor, not hit.t
}

Load and call from Python:

import rayd as rd
import rayd.slang as rs

m = rs.load_module("my_shader.slang")  # use rayd.slang.load_module, not slangtorch.loadModule

scene = rd.Scene()
scene.add_mesh(mesh)
scene.build()

t = m.traceRayT(scene.slang_handle, 0.25, 0.25, -1.0, 0.0, 0.0, 1.0)

Differentiable Slang Example

sceneIntersectAD returns an IntersectionAD with analytic gradients dt_do (∂t/∂origin) and dt_dd (∂t/∂direction):

import rayd_slang;

export IntersectionAD traceAD(uint64_t sceneHandle,
                              float ox, float oy, float oz,
                              float dx, float dy, float dz)
{
    SceneHandle scene = makeSceneHandle(sceneHandle);
    Ray ray = makeRay(float3(ox, oy, oz), float3(dx, dy, dz));
    return sceneIntersectAD(scene, ray);
}

Use it from Python with torch.autograd:

import torch
import rayd as rd
import rayd.slang as rs

m = rs.load_module("my_shader.slang")
scene = rd.Scene()
scene.add_mesh(mesh)
scene.build()

class DiffTrace(torch.autograd.Function):
    @staticmethod
    def forward(ctx, oz):
        ctx.save_for_backward(oz)
        hit = m.traceAD(scene.slang_handle, 0.25, 0.25, oz.item(), 0, 0, 1)
        return torch.tensor(hit.t, device=oz.device)

    @staticmethod
    def backward(ctx, g):
        oz, = ctx.saved_tensors
        hit = m.traceAD(scene.slang_handle, 0.25, 0.25, oz.item(), 0, 0, 1)
        return torch.tensor(hit.dt_do.z * g.item(), device=oz.device)

oz = torch.tensor(-1.0, device="cuda", requires_grad=True)
t = DiffTrace.apply(oz)
t.backward()
print(f"t={t.item()}, dt/doz={oz.grad.item()}")  # t=1.0, dt/doz=-1.0

load_module() runs slangc -target cpp, auto-generates pybind11 bindings, and links against rayd_core. See docs/slang_interop.md for the full compilation pipeline, API reference, and known workarounds.

Edge Acceleration Structure

RayD also provides a scene-level edge acceleration structure.

This is useful for:

  • edge sampling
  • nearest-edge queries
  • visibility-boundary terms
  • geometric edge diffraction models

Scene.set_edge_mask(mask) filters this secondary-edge BVH in scene-global edge index space. It does not modify scene.edge_info(), scene.edge_topology(), scene.mesh_edge_offsets(), or primary-edge camera sampling.

In other words, RayD is not limited to triangle hits. It also gives you direct access to edge-level geometry queries, which are important in many non-graphics simulators.

Compiling Locally

RayD is a Python package with a C++/CUDA extension.

You need Python >=3.10, CUDA Toolkit >=11.0, CMake, a C++17 compiler, drjit>=1.3.1, nanobind==2.9.2, and scikit-build-core.

On Windows, use Visual Studio 2022 with Desktop C++ tools. On Linux, use GCC or Clang with C++17 support.

Recommended environment

conda create -n myenv python=3.10 -y
conda activate myenv
python -m pip install -U pip setuptools wheel
python -m pip install cmake scikit-build-core nanobind==2.9.2
python -m pip install "drjit>=1.3.1"

Install

conda activate myenv
python -m pip install .

Dependencies

RayD depends on:

  • Python 3.10+
  • Dr.Jit 1.3.1+
  • OptiX 8+

RayD does not include:

  • BSDFs
  • emitters
  • integrators
  • scene loaders
  • image I/O
  • path tracing infrastructure

That is by design.

Repository Layout

Testing

python -m unittest tests.drjit.test_geometry -v

Optional PyTorch wrapper tests:

python -m unittest tests.torch.test_geometry -v

Optional Slang interop and gradient tests (requires slangtorch):

python -m unittest tests.slang.test_slang -v

Credits

RayD is developed with reference to:

Citation

@inproceedings{chen2026rfdt,
  title     = {Physically Accurate Differentiable Inverse Rendering
               for Radio Frequency Digital Twin},
  author    = {Chen, Xingyu and Zhang, Xinyu and Zheng, Kai and
               Fang, Xinmin and Li, Tzu-Mao and Lu, Chris Xiaoxuan
               and Li, Zhengxiong},
  booktitle = {Proceedings of the 32nd Annual International Conference
               on Mobile Computing and Networking (MobiCom)},
  year      = {2026},
  doi       = {10.1145/3795866.3796686},
  publisher = {ACM},
  address   = {Austin, TX, USA},
}

License

BSD 3-Clause. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rayd-0.3.0.tar.gz (1.6 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rayd-0.3.0-cp313-cp313-win_amd64.whl (4.1 MB view details)

Uploaded CPython 3.13Windows x86-64

rayd-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

rayd-0.3.0-cp312-cp312-win_amd64.whl (4.1 MB view details)

Uploaded CPython 3.12Windows x86-64

rayd-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

rayd-0.3.0-cp311-cp311-win_amd64.whl (4.1 MB view details)

Uploaded CPython 3.11Windows x86-64

rayd-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

rayd-0.3.0-cp310-cp310-win_amd64.whl (4.1 MB view details)

Uploaded CPython 3.10Windows x86-64

rayd-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file rayd-0.3.0.tar.gz.

File metadata

  • Download URL: rayd-0.3.0.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rayd-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5aac402ad04f33aa363d0648098ce1eaab9c9d4fe873056e3a484873f0bc4016
MD5 913e16187cefb0d987eed82d845f4018
BLAKE2b-256 e9483ffad66dfcff2fac0df8b61ae18dd599ef459bb1e45b80d47cb808c4d469

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0.tar.gz:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: rayd-0.3.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rayd-0.3.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 3b3bf3cd2124532862766f3c915f7687ad22ef0e3ffce12d5fdd89d968a3c51d
MD5 233ec021f1bea65125d05c3c36339f29
BLAKE2b-256 6d00488a9ba2e8a42caf5bdf563d8ff8e540b08a2980ff4f980bbc9a8aba50f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp313-cp313-win_amd64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for rayd-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 46330963770e0e45407a181c52c8d5e7b5d769f12f3c979cf8671606b3ea5ebf
MD5 b2eb6a9d249128159eae38b0d20f04a4
BLAKE2b-256 83576ae47dcdf59f095bd45cd6437ae8d17e747f79a9963cff0bb72b5e8bbf18

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: rayd-0.3.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rayd-0.3.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d1cd558969c006116cde14e90d785e778caeacddaf4f20eb8d18acac84f6270b
MD5 dccc174178e36e503946bbfcaa5afd49
BLAKE2b-256 5be1653edaf0b4ebc5f749f7b6be4ea890eb0e6c66169476d53cdd3ff67b2fe6

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp312-cp312-win_amd64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for rayd-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c2a673bed65949080eec43dbe3303ae9f07568b0af9b24f6da6255048aa0cb69
MD5 dd8b353afebe29250403cd0b2edd521e
BLAKE2b-256 05cc8debf824edf82e911a5d8019e809e051e996eb6a53b10e13b426cc45a8a0

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: rayd-0.3.0-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rayd-0.3.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 27b9c97e2e777588947bd1f2e53dabc31e14dccc0f1a22982cd4efc274c68770
MD5 29933e66c5c20f9ace86ad4b528d0a2d
BLAKE2b-256 dee002de401df7a4d30cdc9d80d12b9549a9166b1637d5e05ca1bea506ef4fea

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp311-cp311-win_amd64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for rayd-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2ce3ddc0faaecabf664c50e706d03e47038c2338f8ab05cda33d06842e64b65f
MD5 28933234dfb82ddc340a0f478cff9856
BLAKE2b-256 90046035ad174d913f0e395426e529a2d0ccc1868ff2a0223e2e5e5b6eb57f75

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: rayd-0.3.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rayd-0.3.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 33bd6ef20f6f2de50a07e4f5c963e96c3c69839487359895681a9761084d6351
MD5 e97bfe32937ff794349b3622c13b087d
BLAKE2b-256 0a8302082a3e0631efa2f7a5b6016969b5a60678eae44bb711b6d4d7412c9abb

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp310-cp310-win_amd64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rayd-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for rayd-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 4295cee3ee7b7066ddffe77129f64d6e57fc45129c3e035d12096a1f827d93c2
MD5 6e74533b584d3d3195bdac6c97bd266a
BLAKE2b-256 7ff3ebe0502bc8b01abaaa97e2682ab0a397c3affa1bbb7e6a519216aff1fb5c

See more details on using hashes here.

Provenance

The following attestation bundles were made for rayd-0.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: pypi.yml on Asixa/RayD

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page