# meshopt_buildMeshlets vs meshopt_buildMeshletsSpatial for Ray Tracing: Key Differences Explained

> Understand meshopt_buildMeshlets vs meshopt_buildMeshletsSpatial for ray tracing. Choose meshopt_buildMeshlets for vertex cache efficiency and meshopt_buildMeshletsSpatial for spatial locality and BVH traversal performance.

- Repository: [Arseny Kapoulkine/meshoptimizer](https://github.com/zeux/meshoptimizer)
- Tags: deep-dive
- Published: 2026-07-12

---

**Use `meshopt_buildMeshlets` for rasterization pipelines that prioritize vertex cache efficiency, and `meshopt_buildMeshletsSpatial` when generating clusters for hardware ray tracing where spatial locality and BVH traversal performance dominate.**

The `meshoptimizer` library provides two distinct algorithms for partitioning triangle meshes into GPU-friendly meshlets. While both `meshopt_buildMeshlets` and `meshopt_buildMeshletsSpatial` generate clusters suitable for modern graphics pipelines, they differ fundamentally in their optimization goals and are designed for different rendering workloads, particularly when targeting ray tracing acceleration structures.

## Core Differences Between the Two Builders

### Optimization Goals

- **`meshopt_buildMeshlets`** balances **topological efficiency** (maximizing vertex reuse) with **culling efficiency** (minimizing meshlet radius and cone divergence). It greedily groups triangles to keep the GPU vertex cache warm while respecting cone culling constraints.

- **`meshopt_buildMeshletsSpatial`** optimizes for **spatial locality** using a **Surface Area Heuristic (SAH)** to create BVH-friendly clusters. It may sacrifice vertex reuse to ensure triangles that are spatially close remain in the same meshlet, reducing ray tracing traversal cost.

### API Parameters and Bounds

The functions expose different parameters that reflect their distinct strategies:

| Function | Key Parameters | Bounds Calculation |
|----------|---------------|-------------------|
| `meshopt_buildMeshlets` | `max_vertices`, `max_triangles`, `cone_weight` | `meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles)` |
| `meshopt_buildMeshletsSpatial` | `max_vertices`, `min_triangles`, `max_triangles`, `fill_weight` | `meshopt_buildMeshletsBound(index_count, max_vertices, min_triangles)` |

**Critical distinction**: When using `meshopt_buildMeshletsSpatial`, you must compute the output buffer size using **`min_triangles`** rather than `max_triangles` because the spatial builder may produce more meshlets by partially filling clusters to preserve spatial coherence.

## Algorithmic Implementation Details

### meshopt_buildMeshlets: Topological Clustering

According to the source in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) (lines 727–740), this algorithm starts from a vertex-cache-optimized index buffer and greedily accumulates triangles into meshlets. It attempts to keep meshlets "complete"—merging disconnected regions if necessary—to maximize vertex reuse. This approach is implemented in [`src/meshletutils.cpp`](https://github.com/zeux/meshoptimizer/blob/main/src/meshletutils.cpp) and is ideal for rasterization pipelines where vertex cache hit rates directly impact performance.

### meshopt_buildMeshletsSpatial: SAH-Based Spatial Clustering

As defined in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) (lines 741–757), this builder recursively subdivides the triangle set using a Surface Area Heuristic similar to BVH construction. It may leave meshlets **partially filled** to preserve spatial coherence, trading some vertex reuse for better ray tracing performance. The `fill_weight` parameter (0.0 to 1.0) controls how aggressively the algorithm fills partially empty meshlets versus maintaining strict spatial locality.

## Code Examples

### Standard Meshlet Generation (Rasterization)

```cpp
#include "meshoptimizer.h"
#include <vector>

// Assume indices, vertex_positions, and vertex_count are defined
const size_t max_vertices = 64;
const size_t max_triangles = 124;
const float cone_weight = 0.0f;

// Calculate maximum possible meshlets
const size_t max_meshlets = meshopt_buildMeshletsBound(
    indices.size(), max_vertices, max_triangles);

std::vector<meshopt_Meshlet> meshlets(max_meshlets);
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);

// Build meshlets optimized for vertex reuse
const size_t meshlet_count = meshopt_buildMeshlets(
    meshlets.data(),
    meshlet_vertices.data(),
    meshlet_triangles.data(),
    indices.data(),
    indices.size(),
    reinterpret_cast<const float*>(vertex_positions),
    vertex_count,
    sizeof(float) * 3,
    max_vertices,
    max_triangles,
    cone_weight);

```

### Ray Tracing Optimized Meshlets (Spatial)

```cpp
#include "meshoptimizer.h"
#include <vector>

// Parameters for ray-tracing-friendly meshlets
const size_t max_vertices = 64;
const size_t min_triangles = 32;   // Enforce minimum to avoid tiny clusters
const size_t max_triangles = 124;
const float fill_weight = 0.0f;    // Prefer spatial coherence over density

// Note: Use min_triangles for bound calculation, not max_triangles
const size_t max_meshlets = meshopt_buildMeshletsBound(
    indices.size(), max_vertices, min_triangles);

std::vector<meshopt_Meshlet> meshlets(max_meshlets);
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);

// Build meshlets optimized for spatial locality and BVH traversal
const size_t meshlet_count = meshopt_buildMeshletsSpatial(
    meshlets.data(),
    meshlet_vertices.data(),
    meshlet_triangles.data(),
    indices.data(),
    indices.size(),
    reinterpret_cast<const float*>(vertex_positions),
    vertex_count,
    sizeof(float) * 3,
    max_vertices,
    min_triangles,
    max_triangles,
    fill_weight);

```

## When to Use Which for Ray Tracing

**Choose `meshopt_buildMeshletsSpatial`** when:
- Targeting Vulkan Ray Tracing or DirectX Raytracing pipelines
- Building acceleration structures on-the-fly or preprocessing assets for RT cores
- Ray traversal cost dominates over vertex shading performance
- You need clusters that map efficiently to BVH leaf nodes

**Choose `meshopt_buildMeshlets`** when:
- Working with traditional rasterization pipelines
- Vertex cache efficiency is the primary bottleneck
- Using meshlets for visibility buffer rendering or GPU culling systems where cone culling is beneficial

## Key Source Files

The implementations and declarations for both functions are located in:

- **[`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h)**: Public API declarations for both builders (lines 727–757)
- **[`src/meshletutils.cpp`](https://github.com/zeux/meshoptimizer/blob/main/src/meshletutils.cpp)**: Core algorithm implementation including SAH-based clustering
- **[`demo/main.cpp`](https://github.com/zeux/meshoptimizer/blob/main/demo/main.cpp)**: Working examples demonstrating both meshlet generation paths around line 907
- **[`README.md`](https://github.com/zeux/meshoptimizer/blob/main/README.md)**: High-level documentation describing the spatial builder's purpose (around line 282)

## Summary

- **`meshopt_buildMeshlets`** optimizes for vertex reuse and cone culling, making it ideal for rasterization pipelines.

- **`meshopt_buildMeshletsSpatial`** uses SAH-based recursive subdivision to prioritize spatial locality, producing meshlets that reduce BVH traversal cost in ray tracing applications.

- Always calculate buffer bounds using **`min_triangles`** (not `max_triangles`) when calling `meshopt_buildMeshletsSpatial` to account for partially filled clusters.

- The **`fill_weight`** parameter controls the trade-off between meshlet density and spatial coherence in the spatial builder.

## Frequently Asked Questions

### Can I use `meshopt_buildMeshlets` for ray tracing workloads?

While functional, `meshopt_buildMeshlets` prioritizes vertex reuse over spatial locality, which can produce irregularly shaped clusters that hurt BVH traversal performance. For dedicated ray tracing pipelines, `meshopt_buildMeshletsSpatial` is strongly recommended as it generates clusters that behave similarly to BVH leaves, reducing traversal cost.

### Why does `meshopt_buildMeshletsSpatial` require `min_triangles` for the bounds calculation?

Because the spatial builder uses SAH-based recursive subdivision to preserve locality, it may split clusters before they reach `max_triangles`, creating partially filled meshlets. This can result in more total meshlets than the worst-case calculation with `max_triangles` would suggest. Using `min_triangles` ensures you allocate sufficient buffer space for these additional sparse clusters.

### What is the optimal `fill_weight` value for ray tracing?

A `fill_weight` of **0.0f** prioritizes spatial coherence over meshlet density, which is typically best for ray tracing as it maintains the SAH-friendly distribution. Values closer to **1.0f** aggressively fill partial meshlets, increasing vertex reuse but potentially degrading BVH traversal efficiency. For pure ray tracing assets, start with **0.0f** and profile traversal cost.