The 3D Handbook
Key term

Parallax occlusion mapping

A pixel-shader technique that marches the view ray through a height map to fake deep surface relief on flat polygons; how the ray march works, step counts and their cost, self-shadowing and depth offset, the flat silhouette, and how POM compares with tessellation and Nanite displacement.

materialsshadersheightoptimisation

Parallax occlusion mapping (POM) makes a flat surface look deep. For every pixel, the shader follows the view ray down into a height and displacement maps until it hits the stored surface, then shades the pixel with the texture coordinates of that hit point. Bricks appear to stand proud of the mortar and hide it behind them as you walk past, while the polygon underneath stays flat.

Natalya Tatarchuk's 2006 paper, which popularised the technique for games, describes it as "per-pixel ray tracing for inverse displacement mapping with an easy-to-implement, efficient algorithm" (Research) 2006). The height and displacement maps page compares POM with plain parallax and real displacement; this page explains how POM works and what it costs.

#How the ray march works

Side view. A straight line at the top is the real flat polygon surface. Below it, a wavy filled shape is the height field stored in the texture, with a dashed line at the bottom marking height zero. An orange view ray enters the flat surface from the upper right and continues down as a dashed line, with sample circles at evenly spaced depth layers. The first circles are hollow, meaning above the height field; the fifth is filled, labelled "first sample below the surface: refine, then shade with this point's UV". An arrow along the top surface shows the UV offset between where the ray entered and where it hit. Text explains that more layers give finer steps but each layer is another texture read.
POM steps along the view ray in equal depth layers until a sample falls below the height field, then uses the texture coordinates of that point.Diagram by the handbook author
  1. The shader takes the view direction in tangent space and MikkTSpace and splits the depth range into layers.
  2. It steps along the ray, one layer at a time, reading the height map at each step. Tatarchuk samples "using a linear search and approximating the height profile as a piecewise linear curve" (Research) 2006).
  3. When a sample falls below the stored height, the ray has entered the surface. The shader refines the hit between the last two samples.
  4. The hit point's UVs are used to read the base colour, normal and other maps. The visible result is that tall features hide what is behind them.

Steep viewing angles need more steps, because the ray travels further across the texture. Tatarchuk varies the sampling rate "as a linear function of the angle between the geometric normal and the view direction ray" (Research) 2006). Godot does the same: its shader mixes between a maximum and a minimum layer count by view angle, with defaults of 32 and 8 layers (Godot Engine: scene/resources/material.cpp).

#What it costs

Each step is a texture read, in the pixel shader, for every pixel the surface covers.

Common ways to keep it affordable:

#Self-shadowing and depth

Two additions make the relief more believable, both at extra cost:

#Limits

  • The silhouette stays flat. POM never changes geometry, so edges and the line where a POM floor meets a wall stay straight. Tatarchuk notes that silhouettes remain low-resolution, and that artists sometimes add border geometry to hide this (Research) 2006).
  • Stair-stepping and swimming. "Parallax occlusion mapping is a sampling-based algorithm at its core, and as such, it can exhibit aliasing" (Research) 2006). Too few layers show as slices at grazing angles; too much height scale makes the surface swim. Godot's height scale defaults to 5 cm between black and white and warns that higher values may produce artefacts (Godot Engine: BaseMaterial3D class reference).
  • Shadows from other objects ignore the relief. Cast shadows use the flat polygon, so the community tutorial suggests you may need to turn off shadow casting on the mesh (Unreal Community Wiki: Parallax Occlusion Mapping).
  • UV layout matters. Godot's heightmap needs normalised heights and correct tangents; if it "looks strange when the camera moves", try flipping the tangent or binormal (Godot Engine: BaseMaterial3D class reference).

#In different tools

#Further reading and watching

Related: height and displacement maps, normal map, height blending, shaders for artists, virtualised geometry.

References

  1. Natalya Tatarchuk (ATI Research) · Practical Parallax Occlusion Mapping with Approximate Soft Shadows for Detailed Surface Rendering · SIGGRAPH 2006 course: Advanced Real-Time Rendering in 3D Graphics and Games, 2006 · paper · accessed 2026-10-21
  2. Godot Engine contributors · scene/resources/material.cpp (BaseMaterial3D shader code) · Godot Engine (GitHub) · docs · accessed 2026-10-21
  3. Godot Engine contributors · BaseMaterial3D class reference (source) · Godot Engine (GitHub) · docs · accessed 2026-10-21
  4. Unreal Engine Community Wiki contributors · Parallax Occlusion Mapping (legacy community tutorial) · Unreal Community Wiki · web · accessed 2026-10-21
  5. Unity Technologies · Lit material Inspector reference (HDRP) · High Definition Render Pipeline manual 17.0 · docs · accessed 2026-10-21

Last updated 2026-10-22