Achieving "reach" within a shader typically involves calculating distances and using these distances to modulate an effect's intensity or applicability. The specifics depend on what "reach" signifies in your context (e.g., light falloff, fog density, interaction radius).
Core Technique: Distance Calculation
The foundation is often the calculation of a distance between two points:
- Fragment to a Specific Point: Calculate the distance from the current fragment's position to a defined point in space (e.g., a light source, an effect's origin).
You'll typically need the fragment's world position (passed from the vertex shader) and a uniform representing the point of interest's world position.
float distanceToPoint = length(fragmentWorldPosition - effectOriginWorldPosition);
- Fragment to Camera (Depth): For effects like fog, "reach" is often related to the distance from the camera. This can be the Z-component of the fragment's position in view space, or the length of the vector from the camera's world position to the fragment's world position.
float distanceToCamera = length(fragmentWorldPosition - cameraWorldPosition);
Alternatively, using view-space Z (often available or easily calculable after vertex transformation):
float viewDepth = abs(fragmentViewPosition.z);
Controlling Reach Parameters via Uniforms
To make the reach dynamic and controllable from your application, use uniform variables:

uniform float u_maxReachDistance;
// The maximum distance the effect extends.uniform float u_startReachDistance;
// (Optional) The distance at which the effect begins to fade in or take effect.uniform float u_falloffExponent;
// (Optional) Controls the curve of the falloff.
Applying the Reach: Normalization and Falloff
Once you have a distance, you typically normalize it to a 0-1 range based on your desired reach parameters. This normalized value then drives the effect.
1. Normalizing the Distance:
The smoothstep
function is excellent for creating a smooth transition between a start and end distance. clamp
can also be used for a harder cutoff.
float normalizedDistance = (distanceToPoint - u_startReachDistance) / (u_maxReachDistance - u_startReachDistance);
float reachFactor = clamp(normalizedDistance, 0.0, 1.0); // Effect increases with distance

For an effect that fades out with distance (stronger when closer):
float reachFactor_fadeOut = 1.0 - clamp(normalizedDistance, 0.0, 1.0);
Using smoothstep
for a smoother transition:
float smoothReachFactor = smoothstep(u_startReachDistance, u_maxReachDistance, distanceToPoint); // 0 before start, 1 after max
For fade-out with smoothstep
:

float smoothReachFactor_fadeOut = 1.0 - smoothstep(u_startReachDistance, u_maxReachDistance, distanceToPoint);
2. Applying Falloff:
The reachFactor
(typically in the 0-1 range) can then be used to interpolate colors, modify intensity, or control other shader properties.
- Linear Falloff: The
reachFactor
itself can be used directly. - Quadratic Falloff (Inverse Square): Common for realistic light attenuation.
float attenuation = 1.0 / (1.0 + falloffConstant distanceToPoint + falloffQuadratic distanceToPoint distanceToPoint);
reachFactor = clamp(attenuation, 0.0, 1.0);
Or, using the normalized distance:
float invNormalized = 1.0 - normalizedDistance; // Assuming normalizedDistance is 0 at start, 1 at end
reachFactor = invNormalized invNormalized; // Quadratic falloff
- Exponential Falloff:
reachFactor = exp(-u_falloffExponent normalizedDistance);
Example: Simple Distance-Based Glow Effect
This shows a glow that is strongest at u_glowCenter
and fades out up to u_glowRadius
.

Input Data (Uniforms & Varyings):
uniform vec3 u_glowCenterWorld;
// World position of the glow's center.uniform float u_glowRadius;
// Maximum reach of the glow.uniform float u_glowIntensity;
// Base intensity of the glow.uniform vec3 u_glowColor;
// Color of the glow.varying vec3 v_fragmentWorldPos;
// Fragment's world position from vertex shader.
Fragment Shader Logic:
glsl
// Calculate distance from fragment to glow center
float dist = length(v_fragmentWorldPos - u_glowCenterWorld);

// Calculate glow factor (0 outside radius, 1 at center, fading linearly)
// smoothstep can provide a smoother falloff near the edge
float glowFactor = 1.0 - clamp(dist / u_glowRadius, 0.0, 1.0);
// For a smoother edge:
// float glowFactor = 1.0 - smoothstep(0.0, u_glowRadius, dist);

// Apply quadratic falloff for a more concentrated glow
glowFactor = glowFactor glowFactor;
// Calculate final glow contribution
vec3 glowContribution = u_glowColor u_glowIntensity glowFactor;
// Add to base color (assuming 'baseColor' is defined)

// vec3 finalColor = baseColor + glowContribution;
// gl_FragColor = vec4(finalColor, 1.0);
Key Considerations
- Coordinate Space: Ensure all positions used in distance calculations are in the same coordinate space (usually world space for global effects, or view space for camera-relative effects).
- Performance:
length()
involves a square root, which can be expensive. If only comparing distances, work with squared distances (dot(vec, vec)
) until the final step to avoid unnecessary square roots. Vertex shader calculations can also offload some work from the fragment shader. - Precision: For very large or very small distances, floating-point precision issues might arise. This is more common in scenarios dealing with vast scales.
- Nature of "Reach": The method to determine reach can vary. It might be a simple radius (spherical), a distance along a specific axis, or a more complex shape. The core math will adapt accordingly. For instance, reach along a plane might involve projecting points onto that plane first.