# How to Implement Ray Tracing Using Mesh and Accel Acceleration Structures in Luisa Compute

> Learn to implement ray tracing in Luisa Compute using Mesh and Accel acceleration structures. Upload buffers, wrap in Accel, build, and query intersect() within kernels.

- Repository: [LuisaGroup/luisacompute](https://github.com/luisagroup/luisacompute)
- Tags: how-to-guide
- Published: 2026-03-06

---

**To implement ray tracing in Luisa Compute, you upload vertex and index buffers to create a `Mesh`, wrap it in an `Accel` structure with instance transforms, build both structures, then query the `AccelVar` inside a kernel using `intersect()`.**

Luisa Compute is a high-performance, data-parallel GPU computing framework developed by the Luisa Group. Implementing hardware-accelerated ray tracing requires understanding its two-level acceleration structure system: the bottom-level `Mesh` for geometry and the top-level `Accel` for scene instances.

## Core Architecture of Ray Tracing in Luisa Compute

The ray tracing pipeline in Luisa Compute separates geometry definition from scene instantiation:

- **`Mesh`** – Represents a vertex-buffer/index-buffer pair. It validates geometry layouts and emits build commands to construct the bottom-level acceleration structure (BLAS). Implemented in [`src/runtime/rtx/mesh.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/mesh.cpp) via `Mesh::build()`.

- **`Accel`** – The top-level acceleration structure (TLAS) that stores instances of meshes, curves, or procedural primitives. Manages instance metadata, visibility masks, and transform matrices. Implemented in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp) via `Accel::emplace_back()` and `Accel::build()`.

- **`AccelVar`** – The DSL-level handle passed into kernels. Exposes the `intersect()` method used to query the TLAS during ray traversal.

## Step-by-Step Implementation Guide

The following complete example is derived from the official test suite in [`src/tests/test_rtx.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/tests/test_rtx.cpp). It demonstrates device initialization, geometry upload, acceleration structure construction, and kernel dispatch.

### Initialize Device and Upload Geometry

First, create a device context and upload your vertex and index data to GPU buffers:

```cpp
#include <luisa/luisa-compute.h>
#include <luisa/dsl/sugar.h>

using namespace luisa;
using namespace luisa::compute;

int main(int argc, char *argv[]) {
    Context ctx{argv[0]};
    Device dev = ctx.create_device(argv[1]);  // "cuda", "metal", etc.
    
    // Define triangle geometry
    std::array<float3> verts{
        float3{-0.5f, -0.5f, 0.0f},
        float3{ 0.5f, -0.5f, 0.0f},
        float3{ 0.0f,  0.5f, 0.0f}};
    std::array<uint> inds{0, 1, 2};
    
    // Upload to GPU
    Buffer<float3> vbuf = dev.create_buffer<float3>(3);
    Buffer<Triangle> ibuf = dev.create_buffer<Triangle>(1);
    Stream s = dev.create_stream();
    s << vbuf.copy_from(verts.data())
      << ibuf.copy_from(inds.data());

```

### Build the Mesh Acceleration Structure

Wrap the buffers in a `Mesh` object and build the bottom-level structure:

```cpp
    // Build Mesh (BLAS)
    Mesh mesh = dev.create_mesh(vbuf, ibuf);
    s << mesh.build();  // Emits MeshBuildCommand, see mesh.cpp

```

The `Mesh::build()` method in [`src/runtime/rtx/mesh.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/mesh.cpp) validates the vertex and index layouts using `detail::check_mesh_*` functions before issuing the build command.

### Create the Top-Level Acceleration Structure (Accel)

Instantiate the mesh one or more times with transforms:

```cpp
    // Create TLAS and add instances
    Accel accel = dev.create_accel();
    accel.emplace_back(mesh, scaling(1.5f));  // First instance, scaled
    
    // Second instance with translation and rotation
    float4x4 transform = translation(float3{-0.25f, 0.0f, 0.1f}) *
                         rotation(float3{0, 0, 1}, 0.5f);
    accel.emplace_back(mesh, transform);
    
    s << accel.build();  // Generates BVH, see Accel::build in accel.cpp

```

The `Accel::emplace_back()` method in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp) supports overloads for meshes, curves, procedural primitives, and motion instances.

### Write the Ray Tracing Kernel

Define a kernel that accepts an `AccelVar` and traces rays:

```cpp
    // Ray tracing kernel
    Kernel2D rt = [&](BufferFloat4 out, AccelVar as, UInt frame) noexcept {
        UInt2 id = dispatch_id().xy();
        Float2 uv = (make_float2(id) + make_float2(0.5f)) / 
                    make_float2(dispatch_size().xy());
        
        // Camera setup
        Float3 ro = make_float3(0.0f, 0.0f, -2.0f);
        Float3 rd = normalize(make_float3(uv * 2.0f - 1.0f, 1.0f));
        
        Ray ray = make_ray(ro, rd);
        auto hit = as.intersect(ray, {});  // Query TLAS
        
        // Shade: blue sky if miss, red if hit
        Float3 col = hit->miss() ? make_float3(0.3f, 0.5f, 0.7f)
                                 : make_float3(1.0f, 0.0f, 0.0f);
        out.write(id.y * dispatch_size_x() + id.x, make_float4(col, 1.0f));
    };

```

The `as.intersect(ray, {})` call maps to the underlying hardware ray-tracing API (e.g., NVIDIA RTX) via the `AccelVar` DSL wrapper.

### Dispatch and Render

Compile the kernel and dispatch it:

```cpp
    // Compile and execute
    auto rt_shader = dev.compile(rt);
    Buffer<float4> img = dev.create_buffer<float4>(512 * 512);
    
    for (UInt f = 0; f < 256; f++) {
        s << rt_shader(img, accel, f).dispatch(512, 512);
    }
    s << synchronize();
}

```

## Advanced Usage Patterns

### Dynamic Instance Updates

You can update instance transforms without rebuilding the entire TLAS:

```cpp
// Update transform for instance 0
accel.set_instance_transform(0, new_transform);
s << accel.update_instance_buffer();  // Fast update, see accel.cpp

```

This pattern is implemented in `Accel::update_instance_buffer` in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp), which uses a `PREFER_UPDATE` hint to avoid full BVH reconstruction.

### Supporting Multiple Primitive Types

The `Accel` structure supports heterogeneous scenes:

```cpp
accel.emplace_back(curve, transform, visibility_mask);      // Hair/fur
accel.emplace_back(procedural, transform, user_id);         // Custom AABBs
accel.emplace_back(motion_mesh, transform, time_samples);  // Motion blur

```

These overloads are defined in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp) (lines 46-62).

### Motion Blur and Animation

For motion blur, create a `MotionInstance` from a `Mesh` with per-keyframe vertex buffers:

```cpp
auto motion_mesh = dev.create_motion_instance(mesh, keyframe_count, time_samples);
accel.emplace_back(motion_mesh, transform);

```

The test suite in [`src/tests/test_motion_blur.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/tests/test_motion_blur.cpp) demonstrates this pattern for animated geometry.

## Key Source Files and Implementation Details

| File | Purpose | Key Functions |
|------|---------|---------------|
| [`src/runtime/rtx/mesh.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/mesh.cpp) | Bottom-level geometry construction | `Mesh::build()` – validates buffers and emits `MeshBuildCommand` |
| [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp) | Top-level structure management | `Accel::emplace_back()`, `Accel::build()`, `Accel::update_instance_buffer()` |
| [`src/tests/test_rtx.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/tests/test_rtx.cpp) | Complete working example | Demonstrates setup, kernel dispatch, and instance animation |
| [`include/luisa/runtime/rtx/accel.h`](https://github.com/luisagroup/luisacompute/blob/main/include/luisa/runtime/rtx/accel.h) | Public C++ API | Class definitions for `Accel` and `Mesh` |
| [`include/luisa/runtime/rtx/mesh.h`](https://github.com/luisagroup/luisacompute/blob/main/include/luisa/runtime/rtx/mesh.h) | Mesh public interface | `Mesh` class declaration and buffer requirements |

## Summary

- **Create a `Mesh`** from vertex and index buffers using `dev.create_mesh()`, then call `mesh.build()` to construct the bottom-level acceleration structure.
- **Instantiate geometry** by creating an `Accel` object and adding mesh instances with `accel.emplace_back(mesh, transform)`, supporting multiple transforms and visibility masks.
- **Build the TLAS** by calling `accel.build()`, which generates the top-level BVH used for ray traversal.
- **Trace rays** inside kernels by passing an `AccelVar` to your shader and calling `as.intersect(ray, {})` to query hits against the acceleration structure.
- **Update dynamically** using `accel.set_instance_transform()` and `accel.update_instance_buffer()` to animate scenes without full rebuilds.

## Frequently Asked Questions

### How do I update instance transforms without rebuilding the entire acceleration structure?

Use `accel.set_instance_transform(instance_id, new_transform)` to modify the transform matrix, then submit `accel.update_instance_buffer()` to the stream. This operation is significantly faster than a full rebuild because it updates only the instance buffer metadata while preserving the existing BVH structure, as implemented in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp).

### Can I mix different geometry types in the same acceleration structure?

Yes, the `Accel` class supports heterogeneous scenes. You can add mesh instances, curves, procedural primitives with custom AABBs, and motion instances to the same `Accel` object using the various `emplace_back()` overloads defined in [`src/runtime/rtx/accel.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/runtime/rtx/accel.cpp). Each instance type is tracked with appropriate flags and user IDs for identification inside the ray tracing kernel.

### What is the difference between `Mesh` and `Accel` in Luisa Compute?

`Mesh` represents a single piece of geometry—a combination of vertex and index buffers that defines triangles. It builds the bottom-level acceleration structure (BLAS). `Accel` is the top-level acceleration structure (TLAS) that contains one or more instances of `Mesh` (or other primitives), each with its own transform matrix and visibility properties. You trace rays against the `Accel`, not individual `Mesh` objects directly.

### How do I implement motion blur with the acceleration structures?

Create a `MotionInstance` using `dev.create_motion_instance(mesh, keyframe_count, time_samples)` where you provide multiple vertex buffers representing the geometry at different time steps. Add this motion instance to your `Accel` using `emplace_back()`. Inside the kernel, the hardware interpolation between keyframes occurs automatically when you trace rays against the `AccelVar`, as demonstrated in [`src/tests/test_motion_blur.cpp`](https://github.com/luisagroup/luisacompute/blob/main/src/tests/test_motion_blur.cpp).