Ray Marching in Shadertoy
WASD + Space & Shift to move; QERFZC to rotate
Why did I make this?
A few months ago I was making an effort to learn about shader code, both for work at my indie studio, and as a personal project. In particular I was focused on Unity which uses HLSL (and also Nvidia Cg, which is basically the same language but that's a topic for another time!) as its shading language.
Of course there are other shader languages out there: most notably GLSL, which I wanted to familiarize myself with. Thankfully, there's a really cool website for doing just that: shadertoy.com. Shadertoy is a project by Inigo Quilez and Pol Jeremias, under the name Beautypi, the former of whom has written their own excellent blogs that I found invaluable in this project. Links will be at the bottom of this post.
Cutting my teeth with GLSL was one thing, but I decided to kill two birds with one stone by also learning about ray marching, the oft-forgot middle child of 3D rendering. Ray marching may not get as much attention as ray-tracing or traditional rasterized rendering, but it's plenty interesting in its own right.
Before I get into it, I'll say that I can't speak to this shader's efficiency. If you take anything from this post, it should be the concepts of raymarching, rather than my actual implementation. I'm just doing a post-mortem on my first go at raymarching; others have done it better than me.
Anyway, here's the link to my raymarcher. Feel free to play around with it, edit the code, or even use segments in your own projects. I also went to the trouble of adding some really basic camera movement, which was no small task on it's own.
What is Ray Marching?
As I alluded earlier, there are several major paradigms of rendering. Of particular interest to me are the traditional rasterized, ray/path-tracing, and ray-marching. When I say rasterized, in this instance I mean the collection of real-time rendering techniques that, until very recently, been ubiquotous to the world of video games. Raytracing is generally the prettiest, most physically-correct method here, but it's also the most hardware and time intensive, limiting its use to the world of animated film and the occasional pre-rendered cutscene.
The three work in very different ways. Ray tracing, assuming backwards-tracing, is the process of drawing lines out from the camera, and recording every bounce they make until they hit a light source, and using all the data you collect along the way to choose a color for a given pixel. Rasterized graphics are frankly a little too complicated to talk about here; but suffice to say they can get very good results with dramatically less compute power compared to ray tracing. Ray marching is... weird.
In both ray marching and ray tracing you need to answer the question "How will I know when my ray has hit something?" For ray tracing this typically means doing some means of collision checking; performance intensive, but very accurate. Ray marching answers this by requiring a Distance Estimator function. The DE (referred to in my code as SDF or signed distance function) is exactly what it sounds like: it's a function that takes a position in space and returns the distance that point is from the nearest point on the surface of an object. Using the DE, the ray marcher simply projects its rays forward, interatively, by the distance returned from the function. A ray marcher can be given a maximum depth, or a max number of these iterations it will go through before stopping. Generally, using the number of iterations, and the final distance from the shape, a color is selected to display the shape.
The necessity of a DE creates a fairly major drawback of ray marching as a rendering method: you can only render shapes that you can represent as a mathematic expression. Simple shapes, e.g. spheres, cubes, etc., can be displayed quite easily. DEs for more complicated shapes are not only more difficult to write in the first place, but they are far less performant. This, as well as other shading complications (texturing, for instance) have prevented ray marching from any kind of wide-spread use in video games, film, or really anywhere expect as a curiosity for programmers and mathematicians. That said, with determination you can render impressive things with ray marching. Check out Inigo Quilez's blog (below) to be blown away.
Ray Marching Fractals
Depicting complex scenes from a film or game might not be feasible with ray marching, but it is certainly possible to render detailed shapes with a ray marcher. What we need is a math function that is fairly simple to solve, yet can produce infinite detail. We need a fractal!
As it happens, that's exactly what the DE for my shadertoy ray marcher is! (By default, anyway. I left a few simpler ones in there) I chose a mandelbulb, a sort of 3D representation of the Mandelbrot set.
There's a number of fractals that you could use as a DE, but I'm focusing on the actual renderer here, not so much the object being rendered. Perhaps I'll take a deeper dive into fractals another time.
There's a lot of resources out there on rendering, and ray marching in particular. I highly recommend trying to make one of these; at the very least it will keep you occupied for a couple evenings!
Here's some links to get you started: