Tech News
← Back to articles

Painting with Math: A Gentle Study of Raymarching (2023)

read original related products more articles

Most of my experience writing GLSL so far focused on enhancing pre-existing Three.js/React Three Fiber scenes that contain diverse geometries and materials with effects that wouldn't be achievable without shaders, such as my work with dispersion and particle effects. However, during my studies of shaders, I always found my way to Shadertoy, which contains a multitude of impressive 3D scenes featuring landscapes, clouds, fractals, and so much more, entirely implemented in GLSL. No geometries. No materials. Just a single fragment shader.

One video titled Painting a Landscape with Math from Inigo Quilez pushed me to learn about the thing behind those 3D shader scenes: Raymarching. I was very intrigued by the perfect blend of creativity, code, and math involved in this rendering technique that allows anyone to sculpt and paint entire worlds in just a few lines of code, so I decided to spend my summer time studying every aspect of Raymarching I could through building as many scenes as possible such as the ones below which are the result of these past few months of work. (and more importantly, I took my time to do that to not burnout as the subject can be overwhelming, hence the title πŸ™‚)

In this article, you will find a condensed version of my study of Raymarching to get a gentle head start on building your own shader-powered scenes. It aims to introduce this technique alongside the concept of signed distance functions and give you the tools and building blocks to build increasingly more sophisticated scenes, from simple objects with lighting and shadows to fractals and infinite landscapes.

Before you start πŸ‘‰ This article assumes you have basic knowledge about shaders, noise, and GLSL, or read The Study of Shaders with React Three Fiber.

Snapshots of Math: demystifying the concept of Raymarching If you're already familiar with Three.js or React Three Fiber 3D scenes, you most likely encountered the concepts of geometry, material, and mesh and maybe even built quite a few scenes with those constructs. Under the hood, rendering with them involves a technique called Rasterization, the process of converting 3D geometries to pixels on a screen. Raymarching on the other hand, is an alternative rendering technique to render a 3D scene, without requiring geometries or meshes... Marching rays Raymarching consists of marching step-by-step alongside rays cast from an origin point (a camera, the observer's eye, ...) through each pixel of an output image until they intersect with objects in the scene within a set maximum number of steps. When an intersection occurs, we draw the resulting pixel. That's the simplest explanation I could come up with. However, it never hurts to have a little visualization to go with the definition of a new concept! That's why I built the widget below illustrating: The step-by-step aspect of Raymarching: The visualizer below lets you iterate up to 13 steps.

The rays cast from a single point of origin (bottom panel)

and the intersections of those rays with an object resulting in pixels on the fragment shader (top panel) -0.5 , 0.5 0.5 , 0.5 0 , 0 -0.5 , -0.5 0.5 , -0.5 Step: 0 Notice how the rays that did not intersect with the sphere in the visualizer above resulted in black pixels πŸ‘€. That means there was nothing to draw since there was no intersection with those rays before we reached the maximum amount of steps. Defining the World with Signed Distance Fields The definition I just gave you above is only approximately correct. Usually, when working with Raymarching: We won't go step-by-step with a constant step distance along our rays. That would make the process very long.

We also won't be relying on the intersection points between the rays and the object. Instead, we will use Signed Distance Fields, functions that calculate the shortest distances between the points reached while marching alongside our rays and the surfaces of the objects in our scene. Relying on the distance to a surface lets us define the entire scene with simple math formulas ✨. For each step, calculating and marching that resulting distance along the rays lets us approach those objects until we're close enough to consider we've "hit" the surface and can draw a pixel. The diagrams below showcase this process: Diagram showcasing the Raymarching process of 3 rays beamed from a single point and marching step-by-step by a distance d obtained through a Signed Distance Field Notice how: each step of the raymarching (in green) goes as far as the distance to the object.

if the distance between a point on our rays and the surface of an object is small enough (under a small value Ξ΅) we consider we have a hit (in orange).

if the distance is not under that threshold, we continue the process over and over using our SDF until we reach the maximum amount of steps. By using SDFs, we can define a variety of basic shapes, like spheres, boxes, or toruses that can then be combined to make more sophisticated objects (which we'll see later in this article). Each of these have a specific formula that had to be reverse-engineered from the distance of a point to its surface. For example, the SDF of a sphere is equivalent to: SDF for a sphere centered at the origin of the scene 1 float sdSphere ( vec3 p , float radius ) { 2 return length ( p ) - radius ; 3 } To help you understand why the SDF of a sphere is defined as such, I made the diagram below: Diagram showcasing 3 points, P1, P2, and P3, being respectively, at a positive distance, small distance, and inside a sphere. In it: P1 is at a distance d from the surface that is positive since the distance between P1 and the center of the sphere c is greater than the radius of the sphere r .

... continue reading