# The Difference Between meshopt_generateVertexRemap and meshopt_generatePositionRemap in meshoptimizer

> Understand the difference between meshopt_generateVertexRemap and meshopt_generatePositionRemap. Learn how each function optimizes vertex data for efficient 3D asset pipelines.

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

---

**Both functions generate index remapping tables, but `meshopt_generateVertexRemap` compares the entire vertex payload for complete deduplication while `meshopt_generatePositionRemap` compares only the 12-byte position attribute, making them suitable for distinct optimization pipelines.**

When optimizing geometry in the `zeux/meshoptimizer` library, developers must choose between two remap generators that appear similar but serve different purposes. Understanding the difference between `meshopt_generateVertexRemap` and `meshopt_generatePositionRemap` is critical for correctly deduplicating vertices during mesh preprocessing. These functions operate at different granularities—one treats vertices as identical only when every byte matches, while the other ignores all attributes except spatial coordinates.

## Scope of Comparison: Full Vertex Data vs. Position Only

### Full-Vertex Deduplication with meshopt_generateVertexRemap

The `meshopt_generateVertexRemap` function performs **complete vertex deduplication** by comparing every byte of the vertex structure. As defined in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) (lines 53-63), it treats vertices as equal only if the entire `vertex_size` payload—including padding—is bit-identical. This ensures that all attributes such as normals, UVs, and colors remain synchronized when remapping indices.

### Position-Only Deduplication with meshopt_generatePositionRemap

In contrast, `meshopt_generatePositionRemap` performs **position-only deduplication** by examining only the first 12 bytes of each vertex (the `float3` position). According to the source code in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) (lines 155-156), this function ignores normals, tangents, and other attributes, making it ideal for generating index buffers where only spatial connectivity matters.

## Input Signatures and Return Values

The functions differ significantly in their signatures and return semantics. `meshopt_generateVertexRemap` accepts a raw vertex buffer pointer and returns the count of unique vertices:

```c
size_t meshopt_generateVertexRemap(
    unsigned int* destination,
    const unsigned int* indices,
    size_t index_count,
    const void* vertices,
    size_t vertex_count,
    size_t vertex_size);

```

This return value allows you to allocate a compact vertex buffer sized exactly for the unique entries.

Conversely, `meshopt_generatePositionRemap` takes a dedicated position array and returns `void`:

```c
void meshopt_generatePositionRemap(
    unsigned int* destination,
    const float* vertex_positions,
    size_t vertex_count,
    size_t vertex_positions_stride);

```

Because it returns no count, you must determine the number of unique positions separately if needed, or simply use the remap table directly for re-indexing operations where only positional continuity is required.

## Practical Implementation and Code Examples

### Compacting a Mesh with meshopt_generateVertexRemap

Use this function when preparing a mesh for `meshopt_remapVertexBuffer` to eliminate duplicate vertices across all attributes. In [`src/meshoptimizer.cpp`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.cpp), the implementation hashes the full vertex blob to find duplicates.

```c
// Example: Deduplicating an interleaved vertex buffer
size_t vertexCount = 1024;
size_t vertexSize = 20; // 8-byte position + 12-byte normal
unsigned int* remap = (unsigned int*)malloc(vertexCount * sizeof(unsigned int));

size_t uniqueCount = meshopt_generateVertexRemap(
    remap,
    NULL,           // no indices (unindexed mesh)
    0,
    vertices,
    vertexCount,
    vertexSize);

// uniqueCount can now size the compact buffer

```

### Building Position-Only Index Buffers with meshopt_generatePositionRemap

This function excels in shadow-map generation or hierarchical LOD construction, as seen in [`demo/clusterlod.h`](https://github.com/zeux/meshoptimizer/blob/main/demo/clusterlod.h). When vertex attributes differ but positions coincide—such as split normals on hard edges—this function merges them into a single logical vertex for positional passes.

```c
// Example: Generating a remap for position-only rendering
size_t vertexCount = 1024;
size_t stride = 20; // distance between position starts
unsigned int* posRemap = (unsigned int*)malloc(vertexCount * sizeof(unsigned int));

meshopt_generatePositionRemap(
    posRemap,
    positions,      // pointer to first float3
    vertexCount,
    stride);

// posRemap now maps original indices to position-unique indices

```

## Summary

- **`meshopt_generateVertexRemap`** compares the entire `vertex_size` bytes to find truly identical vertices, returns the unique vertex count, and is defined in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) lines 53-63.
- **`meshopt_generatePositionRemap`** compares only the 12-byte position attribute, returns `void`, and is optimized for position-only passes like shadow mapping.
- Choose the full-vertex variant when all attributes must remain coherent; choose the position-only variant when only spatial connectivity matters.
- Both functions write to a destination remap array that can be consumed by `meshopt_remapIndexBuffer` or similar utilities.

## Frequently Asked Questions

### Can I use meshopt_generatePositionRemap to reduce vertex buffer size?

No. Because `meshopt_generatePositionRemap` returns `void` and does not account for other attributes, it cannot safely resize a full vertex buffer containing normals or UVs. Use `meshopt_generateVertexRemap` for buffer compaction, as it returns the exact count of unique vertices needed for allocation.

### Why does meshopt_generateVertexRemap require the vertex buffer to be contiguous?

The function signature in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) takes `const void* vertices` and `size_t vertex_size`, implying a tightly packed array. The implementation hashes `vertex_size` bytes at each offset, so non-contiguous data or complex striding requires manual packing before calling the function.

### When should I prefer position-only remapping over full-vertex remapping?

Prefer `meshopt_generatePositionRemap` when building connectivity for shadow volumes, depth pre-passes, or hierarchical simplification where attribute splits (like hard-edge normals) should be ignored. The [`demo/clusterlod.h`](https://github.com/zeux/meshoptimizer/blob/main/demo/clusterlod.h) file demonstrates this for level-of-detail generation.

### Do these functions modify the input index buffer?

No. Both functions treat the input arrays as read-only. They write only to the `destination` remap array, leaving your original geometry data intact.