How to Make a Parallax Occlusion Shader in Blender

Parallax occlusion mapping (POM) is a shader technique that simulates detailed 3D depth and surface relief on flat geometry without increasing polygon counts. While Blender does not feature a single native "POM" node, you can construct a high-performance parallax occlusion network inside the Shader Editor using vector math and texture coordinates. This guide provides a direct, step-by-step walkthrough to build an iterative ray-marched parallax occlusion shader suitable for both EEVEE and Cycles.


Step 1: Calculate the Tangent-Space View Vector

Parallax mapping offsets texture coordinates based on the angle between the surface and the camera. To make the effect view-dependent, you must convert the camera view direction into the surface's tangent space.

  1. Add a Texture Coordinate node and a Geometry node.
  2. Add a Vector Transform node:
    • Set the type to Vector.
    • Convert from World to Object.
    • Connect the Incoming output from the Geometry node to the Vector input of the Vector Transform node.
  3. Add a Tangent node (set to UV Map).
  4. Combine the transformed incoming vector with the Tangent and Normal vectors using a Vector Math (Dot Product) network to isolate the \(X, Y,\) and \(Z\) components relative to the surface plane:
    • Calculate \(X = \text{Incoming} \cdot \text{Tangent}\)
    • Calculate \(Y = \text{Incoming} \cdot \text{Bitangent}\) (Bitangent is the Cross Product of Normal and Tangent)
    • Calculate \(Z = \text{Incoming} \cdot \text{Normal}\)
  5. Combine \(X, Y, Z\) into a single vector using a Combine XYZ node, then divide \(X\) and \(Y\) by \(Z\) using a Vector Math (Divide) node to calculate the view slope.

Step 2: Set Up Depth and Scale Controls

  1. Add a Value node and name it Depth Scale. This controls the apparent depth of the effect.
  2. Multiply the normalized tangent-space view vector \((X/Z, Y/Z)\) by the Depth Scale using a Vector Math (Multiply) node.
  3. Invert the result or multiply by \(-1\) so the parallax pushes inward into the surface rather than protruding.

Step 3: Implement Iterative Ray Marching

True parallax occlusion requires stepping through the height map in layers to find the exact intersection point where the view ray hits the virtual surface.

  1. Define Sample Count: Decide on the number of samples (typically between 8 and 32 steps). A higher count reduces visual slicing artifacts at steep angles.
  2. Step Size Calculation:
    • Divide the total depth vector by the sample count (e.g., \(1 / 16\) for a 16-step setup).
    • Divide the UV offset vector by the same sample count to establish the offset increment per step.
  3. Ray-March Node Group:
    • Create a reusable Node Group that takes the Current UV, Current Depth, UV Step, and Depth Step as inputs.
    • Sample your Height/Displacement Map using the Current UV.
    • Compare the sampled height value against the Current Depth using a Math (Less Than) node.
    • Add the UV Step to the Current UV, and add the Depth Step to the Current Depth.
  4. Chain the Steps:
    • Chain multiple copies of this Node Group together in sequence (e.g., 8 to 16 iterations).
    • Pass the output coordinates of each step into the input of the next step until the ray intersects the height map.

Step 4: Apply Linear Interpolation (Occlusion Refinement)

To achieve smooth results without stair-step artifacts, interpolate between the step right before the ray penetrated the surface and the step right after:

  1. Retrieve the height sample before the intersection and the height sample after the intersection.
  2. Calculate the difference between the ray depth and the surface depth for both steps.
  3. Use a Map Range or Mix (Interpolate) node to compute the precise sub-step intersection UV coordinate.

Step 5: Drive Material Textures with Parallax UVs

  1. Take the final interpolated UV coordinate output from your parallax calculation.
  2. Route this modified UV output directly into the Vector input of all your material texture maps:
    • Base Color Map
    • Normal Map (Ensure the normal map uses the modified UV coordinates)
    • Roughness Map
    • Metallic / Ambient Occlusion Maps
  3. Connect the texture outputs into a Principled BSDF shader and connect the shader to the Material Output.

Best Practices and Optimization

  • Clamp UV Boundaries: If tiling is not desired, place a Clamp node on the UV coordinates to prevent repeating edge artifacts.
  • Node Groups: Encapsulate the entire POM network into a custom Node Group with exposed parameters for Depth Scale, Sample Steps, and Height Map Input to reuse it easily across different materials.
  • Performance: For flat surfaces viewed at shallow angles, keep the sample count around 12 to 16 to maintain fast real-time viewport playback in EEVEE while retaining crisp depth fidelity.