Ray Marching

raymarching mountains terrain from shadertoy

Cover image credit: https://www.shadertoy.com/view/4slGD4

Ray marching is a method in which rays are started from camera into the game world. The ray keeps traveling forward in a loop, one step at a time, until it hits a surface. The surface here is not polygon, but a math function.

This is an effective rendering technique & can work entirely in a fragment shader. Following are some good raymarching examples from ShaderToy:

Overview

The algorithm mainly works like in following code example. The ray starts at camera and moves forward in rayDir direction. At each cycle of the loop, it updates its position forward. However, you might have noticed that it does not update by constant; rather, by the factor of some signed distance function.

vec3 rayMarch(vec3 startPoint, vec3 rayDir) {
    float distanceTraveled = 0.0;
    const int MAX_STEPS = 32;
    const float MIN_DISTANCE = 0.001;
    const float MAX_DISTANCE = 1000.0;

    for(int i = 0; i < MAX_STEPS; i++) {
        vec3 currentPosition = startPoint + distanceTraveled * rayDir;
        float nextStepDistance = signedSphereDistance(currentPosition, spherePosition, sphereRadius);

        if(nextStepDistance < MIN_DISTANCE) {
            // if we hit the sphere, return a color
            return vec3(distanceTraveled / float(MAX_STEPS) * 4.0);
        }

        if(distanceTraveled > MAX_DISTANCE) {
            break;
        }
        distanceTraveled += nextStepDistance;
    }
    return vec3(0.0);
}

Signed Distance Functions

These are functions that take a point in space & tell how far is that point from a surface. If we take point as:

vec3 p = vec3(4, 6, 2);

If we want to find the distance between our point to the closest point on a sphere centered at some other position, say v, then distance between sphere’s surface and the current position will be the vector from p to v subtracted by the radius:

vec3 signedSphereDistance(vec3 from, vec3 to, float radius){
  return (from - to) - radius;
}

When we do raymarching, we start a ray and in a loop, we update ray’s position and also calculate distance between nearest surface points. This helps in incrementing the position of our ray, by that amount so we don’t get wasted loop cycles due to constant increment.

Why Called Signed?

It is because when we are inside the surface, the distance will be negative.

More Reading

  1. List of SDFs: https://iquilezles.org/articles/distfunctions/
  2. https://blog.maximeheckel.com/posts/painting-with-math-a-gentle-study-of-raymarching/
  3. https://dev.to/ramislicer/ray-marching-the-unappreciated-cousin-of-ray-tracing-1iad