What Is the GLSL Reflect Function Formula?
In computer graphics and shader programming, the GLSL
reflect function calculates the reflection direction for an
incident vector against a given surface normal. This article breaks down
the mathematical formula behind reflect(I, N), provides the
step-by-step vector derivation, highlights the critical direction
requirements for input vectors, and explains how this operation powers
classic specular illumination models like the Phong reflection
model.
The Mathematical Definition
The built-in GLSL function signature is defined as:
genType reflect(genType I, genType N);According to the OpenGL Shading Language specification, the exact formula evaluated by this function is:
\[R = I - 2.0 \cdot (N \cdot I) \cdot N\]
Where:
- \(I\) is the incident vector pointing toward the surface.
- \(N\) is the unit surface normal vector pointing away from the surface.
- \(N \cdot I\) represents the dot product between the normal and the incident vector.
- \(R\) is the resulting reflected vector pointing away from the surface.
Derivation of the Reflection Formula
To understand why this formula works geometrically, consider an incident ray \(I\) hitting a planar surface with normal \(N\):
- Decomposing the Incident Vector: Any vector \(I\) can be decomposed into two orthogonal components: a component parallel to the normal (\(I_{\parallel}\)) and a component perpendicular to the normal (\(I_{\perp}\)):
\[I = I_{\parallel} + I_{\perp}\]
- Calculating the Parallel Component: The projection of \(I\) onto the unit normal vector \(N\) gives the parallel component:
\[I_{\parallel} = (I \cdot N) N\]
- Calculating the Perpendicular Component: Subtracting the parallel component from \(I\) yields the perpendicular component:
\[I_{\perp} = I - I_{\parallel} = I - (I \cdot N) N\]
- Inverting Across the Surface: Upon ideal specular reflection, the component parallel to the surface tangent (\(I_{\perp}\)) preserves its direction and magnitude, while the component perpendicular to the surface (\(I_{\parallel}\)) reverses direction:
\[R = I_{\perp} - I_{\parallel}\]
- Combining Terms: Substituting the definitions of \(I_{\perp}\) and \(I_{\parallel}\) into the reflection expression yields:
\[R = (I - (I \cdot N) N) - (I \cdot N) N\]
\[R = I - 2.0 \cdot (I \cdot N) N\]
Because the dot product is commutative (\(I \cdot N = N \cdot I\)), this matches the standard GLSL implementation.
Critical Implementation Details
When working with reflect in shaders, two key
requirements ensure accurate visual results:
- Vector Direction Convention: The incident vector \(I\) must point from the source toward the surface point. A frequent bug in lighting implementations occurs when developers pass the light direction vector \(L\) (which conventionally points from the surface toward the light source). Passing \(L\) instead of \(-L\) yields an inverted vector pointing into the surface geometry.
- Normal Vector Normalization: The mathematical derivation assumes that the normal vector \(N\) has a magnitude of 1. If \(N\) is not normalized, the factor of \((N \cdot I) \cdot N\) scales quadratically with the length of \(N\), producing distorted reflection angles and incorrect magnitudes.
Application in Phong Lighting
In the classic Phong reflection model, the specular highlight depends on the alignment between the reflected light vector \(R\) and the viewing vector \(V\):
// Calculate incident light vector from light to fragment
vec3 lightDir = normalize(fragPos - lightPos);
vec3 normal = normalize(vNormal);
// Compute reflection vector
vec3 reflectDir = reflect(lightDir, normal);
// Specular contribution
vec3 viewDir = normalize(cameraPos - fragPos);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);By computing \(R\) directly through
optimized hardware instructions, reflect delivers fast
specular highlights, environment map lookups, and ray tracing
calculations in modern rendering pipelines.