Skip to content
Materials · TechniqueIntermediate

Triplanar mapping

Texture a mesh without UVs by projecting the texture along the three world axes and blending by the normal.

PositionNormalTextureSurface

Cards

One live example to tweak, learn and copy.

How it works

A texture needs coordinates, and usually the mesh brings them: UVs, unwrapped by hand. Some geometry has none worth using. Procedural terrain, voxel caves and sculpted rock change shape too often to unwrap, and a cliff unwrapped once still stretches its texture wherever a face turns steep. Triplanar mapping makes coordinates out of the position instead.

Three projections

The simplest way to texture by position is to drop one axis: texture(t, p.xz) paints the texture straight down onto the ground, like a slide projector above the scene. That looks right on flat ground and terrible on a cliff, where the same texel is smeared down the whole vertical face, because the face is nearly parallel to the projection.

Triplanar does it three times, once along each axis: p.zy for faces that look along x, p.xz for faces that look up or down, p.xy for faces that look along z. Each projection is sharp exactly where the others smear. The surface normal says which one to trust: a face whose normal points mostly up takes the top projection, a wall facing x takes the x projection, and a slope in between blends them.

The weights

The blend weights come from the normal’s components. abs(normal) measures how much the face looks along each axis, whichever side it faces. Two adjustments make that usable:

  • Sharpen. The raw components cross-fade over a wide band, and there two different images overlap as a blurry double exposure. Raising them to a power narrows the band; 4 to 8 is common, higher makes the seam a visible line.
  • Normalize. Divide by the sum so the weights add up to 1. A normal is a unit vector, but its components don’t sum to 1 (at 45° two of them are 0.71 each), so without this the blend gets brighter at corners and edges.

What it costs and what it breaks

Three texture reads instead of one, for every texture in the material: a triplanar material with color, normal and roughness maps makes nine reads. Coordinates come from the position, so the coordinate space matters. In world space, textures line up across separate objects and never stretch when an object is scaled, but they slide when the object moves. In object space they move with it but repeat differently on each scaled copy. Opposite faces share a projection, so one of the two sees the texture mirrored, which hides on rock but shows on text and bricks.

Normal maps need more care than color. Each projection has its own tangent frame, so the sampled normal must be turned into world space per projection before the three are blended, not blended first. Inigo Quilez’s biplanar mapping cuts the reads to two by keeping only the two largest weights, for some extra arithmetic.

Engines ship it ready-made. Godot’s StandardMaterial3D has a Triplanar option for each UV channel, in object space unless World Triplanar is on, and VisualShader has a TextureParameterTriplanar node. Blender’s Image Texture node does it as Box projection, with a Blend value that is the same cross-fade width. Writing it yourself is what lets you change the blend, mix different textures per axis (moss on top faces, bare rock on the sides) or drop to two reads.

Further reading