Ray marching part 1: the unappreciated cousin of ray tracing
Tags: graphics, tutorial
Published: 2020-09-28 02:46:19

## Ray tracing
We all know about [ray tracing](https://en.wikipedia.org/wiki/Ray_tracing_(graphics)), where you shoot out a ray for each pixel and use fancy math to recursively bounce it around and color the pixel. If you repeat this process for every pixel, you can get a lovely rendering of your scene that, if made by a talented artist, can look almost indistinguishable from real life.
<p><a href="https://commons.wikimedia.org/wiki/File:Ray_trace_diagram.svg#/media/File:Ray_trace_diagram.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Ray_trace_diagram.svg/1200px-Ray_trace_diagram.svg.png" alt="Ray trace diagram.svg"></a>
 
Unfortunately, ray tracing has some downsides, namely that it can be insanely slow. A single frame can take hours to render, which may be fine for animated movies since their creator most likely has very powerful computers dedicated to rendering, however it's quite a nuisance for hobbyists since they usually don't have gigantic renderfarms all to themselves.

However, there is another.
## Ray marching
Enter ray marching: the unappreciated cousin of ray tracing.
Ray marching is quite similar to ray tracing, however it has some interesting quirks that make it an interesting option for real-time graphics demos and games. 
First, I should note that ray marching is not a replacement for rasterization nor ray tracing, it's just a cool alternative. 
Alright! Let's get started.
### How it works
Ray marching is, as I said previously, quite similar to ray tracing. The difference is that ray marching "marches" rays along their vectors and then uses distance equations to decide how far to march the ray, or if it has intersected with an object. We can then return stuff from the marching function like the position it intersected at, how many times it marched, or how far it marched.
### How to implement it
Now that you know roughly how it works, let's actually make a ray marcher. We'll use GLSL, so you'll need to set up OpenGL or use something like [Shadertoy](https://www.shadertoy.com/). If you don't feel like following along, here's the [code on Shadertoy](https://www.shadertoy.com/view/tdtczB#)

First of all, we need some stuff at the top of the shader:
```glsl
#version 420
// you can change #version if you want to
// the resolution, set to the window's dimensions.
uniform vec2 resolution;
// uncomment if you are going to implement a movement system
//uniform vec3 camPos;
```

Let's start with the simplest part, a distance estimator. Specifically, a sphere's DE:
```glsl
float sphereDE(vec3 pos, vec3 spherePos, float size) {
	return length(pos - spherePos) - size;
}
```
This one is super simple and pretty self explanatory so I won't really explain it, however keep in mind that other DEs are more complex than this. If you don't feel like spheres are for you than [here's a big list](https://iquilezles.org/www/articles/distfunctions/distfunctions.htm) of other shapes.

Now we can write the marching function which does the actual marching!
```glsl
vec3 march(vec3 origin, vec3 direction) {
    float rayDist = 0.0;
    const int NUM_STEPS = 32; // doesn't matter much right now
    const float MIN_DIST = 0.001; // threshold for intersection
    const float MAX_DIST = 1000.0; // oops we went into space
    
    for(int i = 0; i < NUM_STEPS; i++) {
        vec3 current_pos = origin + rayDist*direction;
        
        // Use our distance estimator to find the distance
        float _distance = sphereDE(current_pos, vec3(0.0), 1.0);
        
        if(_distance < MIN_DIST) {
            // We hit an object! This just adds a subtle shading effect.
            return vec3(rayDist/float(NUM_STEPS)*4.0);
        }
        
        if(rayDist > MAX_DIST) {
            // We have gone too far
            break;
        }
        
        // Add the marched distance to total
        rayDist += _distance;
    }
    // The ray didn't hit anything so return a color (black, for now)
    return vec3(0.0);
}
```
Now we have all we need to raymarch except a main function.
Let's write it:
```glsl
void main( out vec4 fragColor, in vec2 fragCoord ) {
    // normalized coordinates, resolution should be the window/screens pixel dimensions.
    vec2 uv = fragCoord / resolution.xy * 2.0 - 1.0;
    uv = uv / vec2(0.6, 1.0);
    
    // camPos can be a uniform if you want to move around
    vec3 camPos = vec3(0.0, 0.0, -3.0);
    vec3 rayDir = vec3(uv, 1.0);
    
    // march!
    vec3 shaded_color = march(camPos, rayDir);
    
    // set the color!
    fragColor = vec4(shaded_color, 1.0);
}
```
And we are done! Once you've put these together, you should be able to see a circle that subtly gets lighter towards the edges.
#### Improvements
While what we just made was cool and all, it's not _that_ exciting. It's monochrome, dull, and there's not much to it other than a sphere.
We cold make a struct for a ray, and then we'd be able to return all sorts of useful values:
```glsl
struct Ray {float distance; vec3 endPos; float minDist; bool hit;};
```
So here is my final version: 
```glsl
const int NUM_STEPS = 32; // doesn't matter much right now
const float MIN_DIST = 0.001; // threshold for intersection
const float MAX_DIST = 1000.0; // oops we went into space

struct Ray {float totalDist; float minDist; vec3 endPos; bool hit;};

// The distance estimator for a sphere
float sphereDE(vec3 pos, vec3 spherePos, float size) {
	return length(pos - spherePos) - size;
}

Ray march(vec3 origin, vec3 direction) {
    float rayDist = 0.0;
    float minDist = MAX_DIST;
    
    for(int i = 0; i < NUM_STEPS; i++) {
        vec3 current_pos = origin + rayDist * direction;
        
        // Use our distance estimator to find the distance
        float _distance = sphereDE(current_pos, vec3(0.0), 1.0);
        
        minDist = _distance < minDist ? _distance : minDist;
        
        if(_distance < MIN_DIST) {
            // We hit an object!
            return Ray(rayDist, minDist, current_pos, true);
        }
        
        if(rayDist > MAX_DIST) {
            // We have gone too far
            break;
        }
        
        // Add the marched distance to total
        rayDist += _distance;
    }
    return Ray(MAX_DIST, minDist, origin + rayDist * direction, false);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord / iResolution.xy * 2.0 - 1.0;
    
    // scale the ra
	uv = uv / vec2(0.6, 1.0);
    
    vec3 camPos = vec3(0.0, 0.0, -3.0);
    vec3 rayDir = vec3(uv, 1.0);
    
    Ray marched = march(camPos, rayDir);

    vec3 color = marched.hit ? 
        vec3(marched.totalDist/pow(float(NUM_STEPS), 0.8)*4.0) : // shading
    	vec3(0.0) + vec3(pow(clamp(-1.0 * marched.minDist + 1.0, 0.0, 1.0), 4.0) / 2.0); // glow
    
    fragColor = vec4(color,1.0);
}
```
[Here's how it looks!](https://www.shadertoy.com/view/3dccz2)
There are infinity other additions you can make like colors, CSG (boolean operations between DEs), or more shapes to play around with. However my fingers are getting really tired so I'm going to stop here.

I also highly recommend watching [CodeParade's intro video about ray marching](https://www.youtube.com/watch?v=svLzmFuSBhk) as well as his [video about marble marcher](https://www.youtube.com/watch?v=9U0XVdvQwAI), a game made with ray marching and pretty fractals.

[Part 2](https://dev.to/ramislicer/ray-marching-part-2-319o)