← Back to Blog 3d-modeling

Vertex Animation Textures (VAT): Bake Physics and Crowd Simulations into Game-Ready Textures with Blender and Unreal Engine 5

By BitSoul Team5/20/2026Updated 8/2/20266 min read73 views
Vertex Animation Textures (VAT): Bake Physics and Crowd Simulations into Game-Ready Textures with Blender and Unreal Engine 5

Rigid body simulations, cloth physics, and crowd animations look spectacular — until you try to run them in a game engine in real time. Vertex Animation Textures (VATs) solve this by baking every vertex position from a simulation into a texture, then replaying it in a shader at essentially zero CPU cost. If you've been avoiding complex animated effects because of performance budgets, VATs are the technique that changes everything.

What Are Vertex Animation Textures and Why Use Them?

A Vertex Animation Texture stores per-vertex world-space positions (and optionally normals) across every frame of an animation. Instead of running a physics solver at runtime, the GPU simply samples the texture to look up where each vertex should be on any given frame. The result is visually identical to the original simulation but costs almost nothing to render — a crowd of 10,000 animated characters can run with fewer draw calls than a single fully-rigged skeletal mesh.

VATs are ideal for:

The technique works in any engine that supports custom vertex shaders — UE5, Unity, and Godot 4 all support it. This guide focuses on the Blender-to-UE5 pipeline.

Setting Up Your Blender Scene for VAT Baking

Setting Up Your Blender Scene for VAT Baking — illustrated

Before you bake, your scene needs to meet strict requirements or the exported texture will produce garbage results in engine.

Mesh requirements:
- Vertex count must be identical on every frame. This means NO topology changes — avoid simulations that add/remove geometry (fractured meshes with Bullet physics need to be pre-fractured with a fixed piece count).
- Keep vertex count low. VAT textures scale with `vertex_count × frame_count` pixels. A 500-vertex mesh over 60 frames fits in a 512×64 texture. A 5,000-vertex mesh over 120 frames needs 4096×256 — still manageable but watch your VRAM.
- Apply all modifiers except the simulation modifier before baking.

Timeline setup:
- Set your simulation to loop cleanly. For cloth, run at least 20 frames of pre-roll before your loop start so initial conditions settle.
- Target 30 fps for the baked animation; 60 fps is rarely worth the doubled texture size for looping effects.

Bake the simulation to keyframes first:

```python
# In Blender's Python console or a script:
import bpy

obj = bpy.context.active_object
# Bake cloth/rigid body sim to keyframes
bpy.ops.object.paths_calculate()
# Or for point cache: Object > Apply > Visual Geometry to Mesh per frame
# Use a frame-by-frame export script for reliability
```

For complex sims, the most reliable approach is a frame-by-frame mesh export script that writes one OBJ per frame, which you then recombine. Several free Blender addons (SideFX Houdini VAT exporter ports, the community "VAT Exporter" addon) automate this entirely — search the BitSoul marketplace for ready-to-use VAT-baked assets if you want to skip the baking step entirely.

Baking the VAT: Export Settings and Texture Layout

The VAT consists of two textures: a Position texture (RGB = XYZ offset from rest pose in world space) and a Normal texture (RGB = packed normal vector). Both must be EXR or 16-bit PNG — 8-bit will produce visible stepping artifacts.

Python bake script (simplified):

```python
import bpy, bmesh, struct, math
from mathutils import Vector

obj = bpy.context.active_object
scene = bpy.context.scene
vert_count = len(obj.data.vertices)
frame_count = scene.frame_end - scene.frame_start + 1

# Build flat array: [frame0_v0_xyz, frame0_v1_xyz, ..., frame1_v0_xyz, ...]
positions = []
for f in range(scene.frame_start, scene.frame_end + 1):
scene.frame_set(f)
depsgraph = bpy.context.evaluated_depsgraph_get()
eval_obj = obj.evaluated_get(depsgraph)
mesh = eval_obj.to_mesh()
for v in mesh.vertices:
positions.append(v.co.copy())
eval_obj.to_mesh_clear()

# Write to EXR via Blender's image API
# tex_width = vert_count, tex_height = frame_count
img = bpy.data.images.new('VAT_Position', width=vert_count, height=frame_count, float_buffer=True)
pixels = []
for pos in positions:
pixels += [pos.x, pos.y, pos.z, 1.0] # RGBA
img.pixels = pixels
img.filepath_raw = '//vat_position.exr'
img.file_format = 'OPEN_EXR'
img.save()
print(f'Baked {vert_count} verts × {frame_count} frames')
```

Critical export settings:
- Use world space coordinates, not object space, unless your engine shader accounts for the object transform
- Store the bounding box min/max of all vertex positions — you'll need these in UE5 to remap the normalized texture values back to world units
- Export a rest-pose mesh (frame 0) as your static base mesh FBX — this is what UE5 will import as the Static Mesh

| Setting | Value |
|---|---|
| Position texture format | EXR 32-bit float |
| Normal texture format | EXR 16-bit or PNG 16-bit |
| Coordinate space | World space |
| Texture width | Vertex count (power of 2 preferred) |
| Texture height | Frame count (power of 2 preferred) |
| Base mesh | Rest pose, same vert count, exported as FBX |

Importing and Playing VATs in Unreal Engine 5

Importing and Playing VATs in Unreal Engine 5 — illustrated

With your EXR textures and rest-pose FBX in hand, the UE5 setup takes about 15 minutes.

Import steps:
1. Import the rest-pose FBX as a Static Mesh (not Skeletal Mesh)
2. Import both EXR textures. Set Compression Settings → HDR (RGB, no sRGB) on both — this is critical; sRGB compression will destroy your floating-point position data
3. Disable Mip Maps on both VAT textures — you never want filtered mips on data textures

Material setup in UE5 Material Editor:

The core logic is a World Position Offset node chain:

```
TexCoord [UV1 for VAT]
→ AppendVector(U=vertex_index / vert_count, V=time / frame_count)
→ Texture Sample (VAT_Position, Sampler: Explicit)
→ Multiply (by bbox_range)
→ Add (bbox_min)
→ World Position Offset
```

Key material parameters to expose:
- `VATFrameCount` (scalar) — total frames in texture
- `VATPlaybackSpeed` (scalar) — multiplier on Time node
- `VATBBoxMin` / `VATBBoxMax` (vector3) — from your Blender export
- `VATLoop` (bool) — frac() vs clamp on V coordinate

For the vertex index lookup, use the VertexID material node (available in UE5 under Coordinates). Divide by your total vertex count to get a normalized U coordinate that maps each vertex to its column in the VAT texture.

Once the material is set up, instance it per-asset and drive `VATPlaybackSpeed` through Blueprints or Niagara parameter bindings to randomize playback offsets across crowd instances — this prevents all characters from being on the same frame and kills the "clone army" look instantly.

Performance Budgeting and LOD Strategy for VAT Assets

VATs shift cost from CPU/game thread to GPU texture sampling, which is almost always the right trade for background elements. But they're not free — plan your budget carefully.

Texture memory cost:
`tex_width × tex_height × 4 bytes (EXR) × 2 textures (pos + normal)`

A 512×64 VAT pair costs 256 KB — negligible. A 2048×256 pair costs 4 MB per asset. Stream them with standard UE5 texture streaming; their small size means they'll stay resident in even modest VRAM budgets.

LOD recommendations for VAT meshes:
- LOD0: full vertex count, full VAT
- LOD1 (50% distance): decimated mesh + same VAT (vertex index mismatch requires rebaking — instead, pre-bake a separate low-poly VAT)
- LOD2+ (80% distance): swap to a static impostor billboard — no VAT needed at this range

Instancing: Always render VAT meshes with Instanced Static Mesh or Hierarchical Instanced Static Mesh components. VATs are static meshes by definition, so GPU instancing applies fully — thousands of instances render in a single draw call. Pair with UE5's Mass Entity system for crowd scenarios exceeding 50,000 agents.

Find a growing library of pre-baked VAT assets and optimized game-ready meshes at the BitSoul marketplace.

Wrapping Up

Vertex Animation Textures are one of the most underused techniques in indie and mid-size studio pipelines. The bake happens once in Blender, the runtime cost is negligible, and the visual results — hundreds of animated objects filling your world — are genuinely difficult to achieve any other way at this performance point. Set up the pipeline once, and you'll reach for VATs constantly.

Browse pre-baked VAT assets and game-ready 3D models at the BitSoul marketplace and skip straight to the engine integration step.

Tags: blender unreal-engine-5 vertex-animation-textures game-optimization shaders simulation game-assets pbr

Skip the modelling — download it instead

A free BitSoul account gets you 2 game-ready models every month plus 25 AI Engine credits to generate one of your own, no card required. Clean topology, PBR textures, and GLB downloads that drop straight into Unreal, Unity, Godot or Blender — plus OBJ and 3D-printable STL export.

Create a free account → Browse 846 models