1. Ray Marching and Signed Distance Fields
1. Ray Marching and Signed Distance Fields
2
GPU Ray-tracing
1. Use a minimal vertex shader (no
transforms) - all work happens in
the fragment shader
2. Set up OpenGL with minimal
geometry, a single quad
3. Bind coordinates to each vertex,
let the GPU interpolate
coordinates to every pixel
4. Implement raytracing in GLSL:
a. For each pixel, compute the ray
from the eye through the pixel,
using the interpolated
coordinates to identify the pixel
b. Run the ray tracing algorithm
for every ray
3
GPU Ray-tracing
// Window dimensions vec3 getRayDir(
uniform vec2 iResolution; vec3 camDir,
vec3 camUp,
// Camera position vec2 texCoord) {
uniform vec3 iRayOrigin; vec3 camSide = normalize(
cross(camDir, camUp));
// Camera facing direction vec2 p = 2.0 * texCoord - 1.0;
uniform vec3 iRayDir; p.x *= iResolution.x
/ iResolution.y;
// Camera up direction return normalize(
uniform vec3 iRayUp; p.x * camSide
+ p.y * camUp
// Distance to viewing plane + iPlaneDist * camDir);
uniform float iPlaneDist; }
camUp
// ‘Texture’ coordinate of each
// vertex, interpolated across
// fragments (0,0) → (1,1) camDir
in vec2 texCoord;
ca
mS
id
e
4
GPU Ray-tracing: Sphere
Hit traceSphere(vec3 rayorig, vec3 raydir, vec3 pos, float radius) {
float OdotD = dot(rayorig - pos, raydir);
float OdotO = dot(rayorig - pos, rayorig - pos);
float base = OdotD * OdotD - OdotO + radius * radius;
if (base >= 0) {
float root = sqrt(base);
float t1 = -OdotD + root;
float t2 = -OdotD - root;
if (t1 >= 0 || t2 >= 0) {
float t = (t1 < t2 && t1 >= 0) ? t1 : t2;
vec3 pt = rayorig + raydir * t;
vec3 normal = normalize(pt - pos);
return Hit(pt, normal, t);
}
}
return Hit(vec3(0), vec3(0), -1);
}
5
An alternative to raytracing:
Ray-marching
An alternative to classic ray-tracing is
ray-marching, in which we take a
series of finite steps along the ray until
we strike an object or exceed the
number of permitted steps.
● Also sometimes called ray casting
● Scene objects only need to answer,
“has this ray hit you? y/n”
● Great solution for data like height fields
● Unfortunately…
• often involves many steps
• too large a step size can lead to lost
intersections (step over the object)
• an if() test in the heart of a for() loop
is very hard for the GPU to optimize
6
GPU Ray-marching:
Signed distance fields
Ray-marching can be dramatically
improved, to impressive realtime
GPU performance, using signed
distance fields:
1. Fire ray into scene
2. At each step, measure distance field
function: d(p) = [distance to nearest
object in scene]
3. Advance ray along ray heading by
distance d, because the nearest
intersection can be no closer than d
This is also sometimes called ‘sphere tracing’. Early paper:
http://graphics.cs.illinois.edu/sites/default/files/rtqjs.pdf
7
Signed distance fields
An SDF returns the minimum possible float sphere(vec3 p, float r) {
distance from point p to the surface return length(p) - r;
}
it describes.
The sphere, for instance, is the distance float cube(vec3 p, vec3 dim) {
from p to the center of the sphere, vec3 d = abs(p) - dim;
minus the radius. return min(max(d.x,
max(d.y, d.z)), 0.0)
Negative values indicate a sample + length(max(d, 0.0));
inside the surface, and still express }
absolute distance to the surface.
float cylinder(vec3 p, vec3 dim)
{
return length(p.xz - dim.xy)
- dim.z;
}
return
(step < 50) ? illuminate(pos, rayorig) : background;
}
9
Visualizing step count
Final image Distance field
10
Combining SDFs
We combine SDF models by choosing
which is closer to the sampled point.
● Take the union of two SDFs by
taking the min() of their
functions.
● Take the intersection of two
SDFs by taking the max() of their
functions.
● The max() of function A and the
negative of function B will return
the difference of A - B.
By combining these binary operations
we can create functions which describe
very complex primitives.
11
Combining SDFs
min(A, B)
(union)
max(-A, B)
(difference)
distance
max(A, B)
(intersection)
Object
d = zero
centers
12
Blending SDFs
Taking the min(), max(), etc of two SDFs yields a
sharp discontinuity. Interpolating the two SDFs with
a smooth polynomial yields a smooth distance curve,
blending the models:
http://iquilezles.org/www/articles/smin/smin.htm
13
Transforming SDF geometry
To rotate, translate or scale an SDF model, apply the inverse
transform to the input point within your distance function.
Ex:
float sphere(vec3 pt, float radius) {
return length(pt) - radius;
}
// Scale 2x along X
mat4 S = mat4(
vec4(2, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(0, 0, 0, 1));
// Rotation in XY
float t = sin(time) * PI / 4;
mat4 R = mat4(
vec4(cos(t), sin(t), 0, 0),
vec4(-sin(t), cos(t), 0, 0),
vec4(0, 0, 1, 0),
vec4(0, 0, 0, 1));
// Translate to (3, 3, 3)
mat4 T = mat4(
vec4(1, 0, 0, 3),
vec4(0, 1, 0, 3),
vec4(0, 0, 1, 3),
vec4(0, 0, 0, 1));
15
Transforming SDF geometry
The previous example modified ‘all
of space’ with the same transform,
so its distance functions retain
their local linearity.
We can also apply non-uniform
spatial distortion, such as by
choosing how much we’ll modify
space as a function of where in
space we are.
16
Find the normal to an SDF
Finding the normal: local gradient
float d = getSdf(pt);
vec3 normal = normalize(vec3(
getSdf(vec3(pt.x + 0.0001, pt.y, pt.z)) - d,
getSdf(vec3(pt.x, pt.y + 0.0001, pt.z)) - d,
getSdf(vec3(pt.x, pt.y, pt.z + 0.0001)) - d));
17
SDF shadows
Ray-marched shadows are
straightforward: march a ray
towards each light source, don’t
illuminate if the SDF ever drops
too close to zero.
Unlike ray-tracing, soft shadows are
almost free with SDFs: attenuate
illumination by a linear function of
the ray marching near to another
object.
18
Soft SDF shadows
float shadow(vec3 pt) {
vec3 lightDir = normalize(lightPos - pt);
float kd = 1;
int step = 0;
19
Repeating SDF geometry
If we take the modulus of a point’s
position along one or more axes
before computing its signed
distance, then we segment space
into infinite parallel regions of
repeated distance. Space near the
origin ‘repeats’.
With SDFs we get infinite repetition
of geometry for no extra cost.
20
Repeating SDF geometry
float sphere(vec3 pt, float radius) {
return length(pt) - radius;
}
● sdSphere(4, 4)
= √(4*4+4*4) - 1
= ~4.5
● sdSphere(
((4 + 2) % 4) - 2, 4)
= √(0*0+4*4) - 1
= 3
● sdSphere(
((4 + 2) % 4) - 2,
((4 + 2) % 4) - 2)
= √(0*0+0*0) - 1
= -1 // Inside surface
21
SDF - Live demo
22
Recommended reading
Seminal papers:
● John C. Hart, “Sphere Tracing: A Geometric Method for the Antialiased Ray Tracing of Implicit
Surfaces”, http://graphics.cs.illinois.edu/papers/zeno
● John C. Hart et al., “Ray Tracing Deterministic 3-D Fractals”,
http://graphics.cs.illinois.edu/sites/default/files/rtqjs.pdf
23