How Does the Distance Function Work in GLSL?

In OpenGL Shading Language (GLSL), the distance() function calculates the Euclidean spatial separation between two points across arbitrary vector dimensions. This article explores the mathematical formula behind the operation, its underlying hardware execution on modern graphics processing units, syntax variations across floating-point types, and optimization techniques such as avoiding unnecessary square root calculations.

Mathematical Formulation

The distance() function computes the straight-line Euclidean distance between two points, \(P_0\) and \(P_1\). Mathematically, it evaluates the length of the displacement vector formed by subtracting one point from the other:

\[\text{distance}(P_0, P_1) = \Vert{}P_0 - P_1\Vert{}\]

For two \(n\)-dimensional points \(P_0 = (x_0, y_0, \dots)\) and \(P_1 = (x_1, y_1, \dots)\), the distance is expanded using the Pythagorean theorem:

\[\text{distance}(P_0, P_1) = \sqrt{\sum_{i=1}^{n} (P_{0,i} - P_{1,i})^2}\]

In terms of vector operations, this calculation is equivalent to taking the square root of the dot product of the difference vector with itself:

\[\text{distance}(P_0, P_1) = \sqrt{(P_0 - P_1) \cdot (P_0 - P_1)}\]

GLSL Function Signatures

GLSL provides built-in overloads for scalar and vector floating-point types under the genType classification:

GLSL also provides double-precision overloads (genDType) returning double when using precision qualifiers or extensions that support 64-bit floating-point math (dvec2, dvec3, dvec4).

// Example usage in a fragment shader
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
vec2 center = vec2(0.5, 0.5);

// Calculate 2D distance to screen center
float dist = distance(uv, center);

GPU Execution and Hardware Implementation

Graphics processing units are optimized for parallel vector math. When a shader executes distance(p0, p1), the compiler translates the instruction into a tight sequence of low-level SIMD (Single Instruction, Multiple Data) operations:

  1. Vector Subtraction: A component-wise subtraction instruction computes the difference vector \(\Delta = P_0 - P_1\).
  2. Dot Product / Fused Multiply-Add (FMA): Modern GPU architectures feature dedicated hardware instructions (such as DP2, DP3, or DP4) that multiply corresponding components and sum the results in minimal clock cycles.
  3. Square Root: The GPU calculates the square root of the scalar dot product using dedicated Special Function Units (SFUs), typically through reciprocal square root instructions (RSQ) followed by a multiplication step.

Performance Optimization: Squared Distance

While the distance() function is hardware-accelerated, the square root operation remains computationally more expensive than basic arithmetic. In scenarios where you only need to compare distances—such as collision detection, proximity tests, or circular discard masks—computing the squared distance avoids the square root entirely.

// Standard approach (includes square root)
if (distance(fragPos, lightPos) < radius) {
    // Inside light radius
}

// Optimized approach (avoids square root)
vec3 diff = fragPos - lightPos;
if (dot(diff, diff) < radius * radius) {
    // Inside light radius
}

Using dot(diff, diff) calculates \(\Delta \cdot \Delta = \Vert{}\Delta\Vert{}^2\), yielding identical boolean comparison results while eliminating the square root instruction across millions of fragment shader invocations.

Common Applications in Shaders

Spatial distance computation serves as a fundamental building block in real-time computer graphics pipelines: