Triplanar mapping
Texture a mesh without UVs by projecting the texture along the three world axes and blending by the normal.
Cards
One live example to tweak, learn and copy.
How it works
A texture needs coordinates, and usually the mesh brings them: UVsA 2D position on a surface or the screen, usually from 0 to 1 on both axes. Shaders draw shapes and patterns as functions of it. UV coordinates A 2D position on a surface or the screen, usually from 0 to 1 on both axes. Shaders draw shapes and patterns as functions of it.uv = gl_FragCoord.xy / resolution
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: Normal A unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.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
normalA unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions.
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 spaceThe frame a position or direction is measured in, such as object, world, view, clip or screen space. Only values in the same space can be combined. Coordinate space The frame a position or direction is measured in, such as object, world, view, clip or screen space. Only values in the same space can be combined.
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
- Normal (geometry) (opens in a new tab)Reference
- Basic Lighting (opens in a new tab)Article
- Normal Mapping (opens in a new tab)Article
- Coordinate Systems (opens in a new tab)Article
- OpenGL Transformation (opens in a new tab)Article
- gl_FragCoord (opens in a new tab)Reference
- texture (opens in a new tab)Reference
- texelFetch (opens in a new tab)Reference
- Using textures in WebGL (opens in a new tab)Docs
- GPU Gems 3, chapter 1: Generating Complex Procedural Terrains Using the GPU (opens in a new tab)Book
- Biplanar mapping (opens in a new tab)Article
- Standard Material 3D and ORM Material 3D (opens in a new tab)Docs
- VisualShaderNodeTextureParameterTriplanar (opens in a new tab)Reference
- Image Texture Node (opens in a new tab)Docs
- abs (opens in a new tab)Reference
- pow (opens in a new tab)Reference