After having posted about the basics of distance functions in several places (pouet, my blog, shadertoy, private emails, etc), i thought it might make
sense to put these together in centralized place. Here you will find the distance functions for basic primitives, plus the formulas for combining them together
for building more complex shapes, as well as some distortion functions that you can use to shape your objects. Hopefully this will be usefull for those rendering
scenes with raymarching. You can see some of the results you can get by using these techniques in the raymarching distance
fields article. Lastly, this article doesn't include lighting tricks, nor marching acceleartion tricks or more advanced techniques as recursive primitives
or fractals.
primitives
All primitives are centered at the origin. You will have to transform the point to get arbitrarily rotated, translated and scaled objects (see below).
Sphere - signed
float sdSphere( vec3 p, float s )
{
return length(p)-s;
} |
 |
|
Box - unsigned
float udBox( vec3 p, vec3 b )
{
return length(max(abs(p)-b,0.0));
}
|
 |
Round Box - unsigned
float udRoundBox( vec3 p, vec3 b, float r )
{
return length(max(abs(p)-b,0.0))-r;
}
|
 |
|
Box - signed
float sdBox( vec3 p, vec3 b )
{
vec3 di = abs(p) - b;
float mc = maxcomp(di);
return min(mc,length(max(di,0.0)));
}
|
 |
Torus - signed
float sdTorus( vec3 p, vec2 t )
{
vec2 q = vec2(length(p.xz)-t.x,p.y);
return length(q)-t.y;
} |
 |
|
Cylinder - signed
float sdCylinder( vec3 p, vec3 c )
{
return length(p.xz-c.xy)-c.z;
} |
 |
Cone - signed
float sdCone( vec3 p, vec2 c )
{
// c must be normalized
float q = length(p.xy);
return dot(c,vec2(q,p.z));
}
|
 |
|
Plane - signed
float sdPlane( vec3 p, vec4 n )
{
// n must be normalized
return dot(p,n.xyz) + n.w;
}
|
 |
Hexagonal Prism - unsigned
float udHexPrism( vec3 p, vec2 h )
{
float q = abs(p);
return max(q.z-h.y,max(q.x+q.y*0.57735,q.y)-h.x);
}
|
 |
|
|
|
Most of these functions can be modified to use other norms than the euclidean. By replacing length(p), which computes (x^2+y^2+z^2)^(1/2) by (x^n+y^n+z^n)^(1/n) one can get
variations of the basic primitives that have rounded edges rather than sharp ones.
Torus82 - signed
float sdTorus82( vec3 p, vec2 t )
{
vec2 q = vec2(length2(p.xz)-t.x,p.y);
return length8(q)-t.y;
} |
 |
|
Torus88 - signed
float sdTorus88( vec3 p, vec2 t )
{
vec2 q = vec2(length8(p.xz)-t.x,p.y);
return length8(q)-t.y;
} |
 |
distance operations
The d1 and d2 parameters in the following functions are the distance to the two distance fields to combine together.
Union
float opU( float d1, float d2 )
{
return min(d1,d2);
}
|
|
 |
|
Substraction
float opS( float d1, float d2 )
{
return max(-d1,d2);
}
|
|
 |
|
Intersection
float opI( float d1, float d2 )
{
return max(d1,d2);
}
|
|
 |
domain operations
Where "primitive", in the examples below, is any distance formula really (one of the basic primitives aboce, a combination, or a complex distance field).
Repetition
float opRep( vec3 p, vec3 c )
{
vec3 q = mod(p,c)-0.5*c;
return primitve( q );
}
|
|
 |
|
Rotation/Translation
vec3 opTx( vec3 p, mat4 m )
{
vec3 q = invert(m)*p;
return primitive(q);
}
|
|
 |
|
Scale
float opScale( vec3 p, float s )
{
return primitive(p/s)*s;
}
|
|
 |
distance deformations
You must be carefull when using distance transformation functions, as the field created might not be a real distance function anymore. You will probably
need to decrease your step size, if you are using a raymarcher to sample this. The displacement example below is using sin(20*p.x)*sin(20*p.y)*sin(20*p.z) as
displacement pattern, but you can of course use anything you might imagine.
Displacement
float opDisplace( vec3 p )
{
float d1 = primitive(p);
float d2 = displacement(p);
return d1+d2;
}
|
|
 |
|
Blend
float opBlend( vec3 p )
{
float d1 = primitiveA(p);
float d2 = primitiveB(p);
floar dd = smoothcurve(d1-d2);
return mix(d1,d2,dd);
}
|
|
 |
domain deformations
Domain deformation functions do not preserve distances neither. You must decrease your marching step to properly sample these functions (proportionally to the
maximun derivative of the domain distortion function). Of course, any distortion function can be used, from twists, bends, to random noise driven deformations.
Twist
float opTwist( vec3 p )
{
float c = cos(20.0*p.y);
float s = sin(20.0*p.y);
mat2 m = mat2(c,-s,s,c);
vec3 q = vec3(m*p.xz,p.y);
return primitive(q);
}
|
|
 |
|
Cheap Bend
float opCheapBend( vec3 p )
{
float c = cos(20.0*p.y);
float s = sin(20.0*p.y);
mat2 m = mat2(c,-s,s,c);
vec3 q = vec3(m*p.xy,p.z);
return primitive(q);
}
|
|
 |
|