Day 43 - 🔬 Deep dive - Dot Product in Rendering

General / 01 July 2026

The dot product shows up constantly in shaders, usually as a node you drop in without thinking too hard about it. Here is what it actually does and why it is useful.


What It Is

The dot product measures how much two vectors point in the same direction. The result is a single scalar: 1 when they are perfectly aligned, 0 when they are perpendicular, and -1 when they point in opposite directions.

In a shader, that scalar becomes a mask, a value you can use to drive blends, control effects, or make decisions based on geometry and view angle.

source: Unity - example of what World Space Normals and Dot-product can achieve in shaders.


Masking by Surface Angle

The most common use is blending a feature onto surfaces that face a particular direction. The classic example: moss on the upward-facing faces of a rock.

float3 upVector = float3(0, 0, 1); // world up
float mask = dot(upVector, worldSpaceNormal);
mask = saturate(mask);

The dot product returns 1 on faces pointing straight up, 0 on vertical faces, and negative values on downward-facing geometry. Saturate clamps everything below 0 away, leaving a clean mask. No matter how you rotate the asset, the moss always lands on the faces that point upward, the math follows the geometry.


Checking Camera Facing

A less obvious but equally useful application: determining whether a surface is facing toward the camera or away from it.

Compare the world-space normal against the view direction vector. If both vectors point away from the camera, they are in the same direction and the dot product is positive. If the surface is facing toward the viewer, the normal and view vector are opposed, the result is negative. Multiply by -1 and saturate to convert that into a usable [0, 1] mask.

This is useful for controlling effects that should only appear on visible surfaces, rim lighting, edge detection, or suppressing reflections on back-facing geometry.


Be Consistent

The result is only meaningful if both vectors are in the same space. Mixing a world-space up vector with a tangent-space normal will produce incorrect output, and the error won't always be obvious. Before wiring up a dot product, confirm which space each input is in and convert if needed.


Practical Takeaway

The dot product is unclamped by default. For masks that feed into blends or lerps, always add a saturate or clamp afterwards, values below 0 or above 1 will produce unexpected results downstream. If you want a soft transition rather than a hard cutoff, pipe the result into a smoothstep before using it as a mask.


© 2026 Stefan Groenewoud, All views are my own, not those of my employer.