
These examples show a collection of different distance functions for basic 3D primitive shapes and how they can be implemented in a ray marching shader. Keep in mind that many more shapes can be created by combining them together with other distance functions like boolean and smooth blending operators.
All of the distance functions can be found in the first example. The second example in this post only contains the necessary distance functions needed for the example.
For more information on these 3D primitive shape distance functions visit Inigo Quilez’s blog: http://www.iquilezles.org which is the main source of the DF functions.
Trying the examples
If you quickly want to try out the examples I recommend the web based GLSL editor from the book of shaders over here:
http://editor.thebookofshaders.com
If you prefer working with an offline editor I recommend Visual Studio Code with the glsl-canvas extension: https://code.visualstudio.com
Or if you prefer Sublime Text then you can install this package : https://packagecontrol.io/packages/glslViewer
If you are reading this on a iOS device I recommend the app ‘Shaders’ which can be used to program and run GLSL shaders and also has a public timeline that you can use to share your work with others:
https://apps.apple.com/app/shaders-shader-editor/id1221602862?l=en
1. 3D Primitives

3D Primitives
Inside of the GetDist() function the octahedron, link and rounded box are created positioned and rotated on two axes.
To position a primitive we can simply subtract the vec3 position we want the primitive to have from p:oPos = p - vec3(-3,1,7);
To Rotate on one axis at a time we can multiply the position as a vector2 with a matrix2x2:oPos.xy *= Rotate(-u_time);
precision highp float; uniform vec2 u_resolution; // Width & height of the shader uniform float u_time; // Time elapsed // Constants #define PI 3.1415925359 #define MAX_STEPS 100 // Mar Raymarching steps #define MAX_DIST 100. // Max Raymarching distance #define SURF_DIST .01 // Surface Distance mat2 Rotate(float a) { float s = sin(a),c = cos(a); return mat2(c,-s,s,c); } /////////////////////// // Primitives /////////////////////// // Dot2 helper function float dot2( vec2 v ) { return dot(v,v); } float dot2( vec3 v ) { return dot(v,v); } // Sphere - exact float sphereSDF( vec3 p, float s ) { return length(p)-s; } // Box - exact float boxSDF( vec3 p, vec3 b ) { vec3 q = abs(p) - b; return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0); } // Round Box - exact float roundBoxSDF( vec3 p, vec3 b, float r ) { vec3 q = abs(p) - b; return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r; } // Plane - exact float planeSDF( vec3 p, vec4 n ) { // n must be normalized return dot(p,n.xyz) + n.w; } // Hexagonal Prism - exact float hexPrismSDF(vec3 p, vec2 h) { const vec3 k = vec3(-0.8660254, 0.5, 0.57735); p = abs(p); p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy; vec2 d = vec2( length(p.xy-vec2(clamp(p.x,-k.z*h.x,k.z*h.x), h.x))*sign(p.y-h.x), p.z-h.y ); return min(max(d.x,d.y),0.0) + length(max(d,0.0)); } // Triangular Prism - exact float triPrismSDF( vec3 p, vec2 h ) { const float k = sqrt(3.0); h.x *= 0.5*k; p.xy /= h.x; p.x = abs(p.x) - 1.0; p.y = p.y + 1.0/k; if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0; p.x -= clamp( p.x, -2.0, 0.0 ); float d1 = length(p.xy)*sign(-p.y)*h.x; float d2 = abs(p.z)-h.y; return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } //Capsule / Line float capsuleSDF( vec3 p, vec3 a, vec3 b, float r ) { vec3 pa = p - a, ba = b - a; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ) - r; } //Capsule / Line - exact float verticalCapsuleSDF(vec3 p, float h, float r) { p.y -= clamp( p.y, 0.0, h ); return length( p ) - r; } //Ininite Cylinder - exact float cylinderSDF( vec3 p, vec3 c ) { return length(p.xz-c.xy)-c.z; } //Capped Cylinder - exact float cappedCylinderSDF( vec3 p, float h, float r ) { vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(h,r); return min(max(d.x,d.y),0.0) + length(max(d,0.0)); } //Capped Cylinder - exact float cappedCylinderSDF(vec3 p, vec3 a, vec3 b, float r) { vec3 ba = b - a; vec3 pa = p - a; float baba = dot(ba,ba); float paba = dot(pa,ba); float x = length(pa*baba-ba*paba) - r*baba; float y = abs(paba-baba*0.5)-baba*0.5; float x2 = x*x; float y2 = y*y*baba; float d = (max(x,y)<0.0)?-min(x2,y2):(((x>0.0)?x2:0.0)+((y>0.0)?y2:0.0)); return sign(d)*sqrt(abs(d))/baba; } // Rounded Cylinder - exact float roundedCylinderSDF( vec3 p, float ra, float rb, float h ) { vec2 d = vec2( length(p.xz)-2.0*ra+rb, abs(p.y) - h ); return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb; } // Cone - exact float coneSDF( vec3 p, vec2 c ) { // c is the sin/cos of the angle float q = length(p.xy); return dot(c,vec2(q,p.z)); } // Capped Cone - exact //float dot2( vec3 v ) { return dot(v,v); } float cappedConeSDF( vec3 p, float h, float r1, float r2 ) { vec2 q = vec2( length(p.xz), p.y ); vec2 k1 = vec2(r2,h); vec2 k2 = vec2(r2-r1,2.0*h); vec2 ca = vec2(q.x-min(q.x,(q.y<0.0)?r1:r2), abs(q.y)-h); vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 ); float s = (cb.x<0.0 && ca.y<0.0) ? -1.0 : 1.0; return s*sqrt( min(dot2(ca),dot2(cb)) ); } // Capped Cone - exact float cappedConeSDF(vec3 p, vec3 a, vec3 b, float ra, float rb) { float rba = rb-ra; float baba = dot(b-a,b-a); float papa = dot(p-a,p-a); float paba = dot(p-a,b-a)/baba; float x = sqrt( papa - paba*paba*baba ); float cax = max(0.0,x-((paba<0.5)?ra:rb)); float cay = abs(paba-0.5)-0.5; float k = rba*rba + baba; float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 ); float cbx = x-ra - f*rba; float cby = paba - f; float s = (cbx<0.0 && cay<0.0) ? -1.0 : 1.0; return s*sqrt( min(cax*cax+cay*cay*baba, cbx*cbx+cby*cby*baba) ); } // Solid Angle - exact float solidAngleSDF(vec3 p, vec2 c, float ra) { // c is the sin/cos of the angle vec2 q = vec2( length(p.xz), p.y ); float l = length(q) - ra; float m = length(q - c*clamp(dot(q,c),0.0,ra) ); return max(l,m*sign(c.y*q.x-c.x*q.y)); } // Round cone - exact float roundConeSDF( vec3 p, float r1, float r2, float h ) { vec2 q = vec2( length(p.xz), p.y ); float b = (r1-r2)/h; float a = sqrt(1.0-b*b); float k = dot(q,vec2(-b,a)); if( k < 0.0 ) return length(q) - r1; if( k > a*h ) return length(q-vec2(0.0,h)) - r2; return dot(q, vec2(a,b) ) - r1; } // Ellipsoid - bound (not exact!) float ellipsoidSDF( vec3 p, vec3 r ) { float k0 = length(p/r); float k1 = length(p/(r*r)); return k0*(k0-1.0)/k1; } // Torus - exact float torusSDF( vec3 p, vec2 t ) { vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y; } // Capped Torus - exact float cappedTorusSDF(in vec3 p, in vec2 sc, in float ra, in float rb) { p.x = abs(p.x); float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy); return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb; } // Joint - exact // returns distance in .x and UVW parametrization in .yzw vec4 joint3DSphereSDF( in vec3 p, in float l, in float a, in float w) { if( abs(a)<0.001 ) return vec4( length(p-vec3(0,clamp(p.y,0.0,l),0))-w, p ); vec2 sc = vec2(sin(a),cos(a)); float ra = 0.5*l/a; p.x -= ra; vec2 q = p.xy - 2.0*sc*max(0.0,dot(sc,p.xy)); float u = abs(ra)-length(q); float d2 = (q.y<0.0) ? dot2( q+vec2(ra,0.0) ) : u*u; float s = sign(a); return vec4( sqrt(d2+p.z*p.z)-w, (p.y>0.0) ? s*u : s*sign(-p.x)*(q.x+ra), (p.y>0.0) ? atan(s*p.y,-s*p.x)*ra : (s*p.x<0.0)?p.y:l-p.y, p.z ); } // Link - exact float linkSDF(vec3 p, float le, float r1, float r2) { vec3 q = vec3( p.x, max(abs(p.y)-le,0.0), p.z ); return length(vec2(length(q.xy)-r1,q.z)) - r2; } // Octahedron - exact float octahedronSDF(vec3 p, float s) { p = abs(p); float m = p.x+p.y+p.z-s; vec3 q; if( 3.0*p.x < m ) q = p.xyz; else if( 3.0*p.y < m ) q = p.yzx; else if( 3.0*p.z < m ) q = p.zxy; else return m*0.57735027; float k = clamp(0.5*(q.z-q.y+s),0.0,s); return length(vec3(q.x,q.y-s+k,q.z-k)); } // Octahedron - bound (not exact) float octahedron2SDF(vec3 p, float s) { p = abs(p); return (p.x+p.y+p.z-s)*0.57735027; } // Pyramid - exact float pyramidSDF(vec3 p, float h) { float m2 = h*h + 0.25; p.xz = abs(p.xz); p.xz = (p.z>p.x) ? p.zx : p.xz; p.xz -= 0.5; vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y); float s = max(-q.x,0.0); float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 ); float a = m2*(q.x+s)*(q.x+s) + q.y*q.y; float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t); float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b); return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y)); } // Triangle UDF - exact float triangleUDF( vec3 p, vec3 a, vec3 b, vec3 c ) { vec3 ba = b - a; vec3 pa = p - a; vec3 cb = c - b; vec3 pb = p - b; vec3 ac = a - c; vec3 pc = p - c; vec3 nor = cross( ba, ac ); return sqrt( (sign(dot(cross(ba,nor),pa)) + sign(dot(cross(cb,nor),pb)) + sign(dot(cross(ac,nor),pc))<2.0) ? min( min( dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc) ) : dot(nor,pa)*dot(nor,pa)/dot2(nor) ); } // Quad UDF - exact float quadUDF( vec3 p, vec3 a, vec3 b, vec3 c, vec3 d) { vec3 ba = b - a; vec3 pa = p - a; vec3 cb = c - b; vec3 pb = p - b; vec3 dc = d - c; vec3 pc = p - c; vec3 ad = a - d; vec3 pd = p - d; vec3 nor = cross( ba, ad ); return sqrt( (sign(dot(cross(ba,nor),pa)) + sign(dot(cross(cb,nor),pb)) + sign(dot(cross(dc,nor),pc)) + sign(dot(cross(ad,nor),pd))<3.0) ? min( min( min( dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), dot2(dc*clamp(dot(dc,pc)/dot2(dc),0.0,1.0)-pc) ), dot2(ad*clamp(dot(ad,pd)/dot2(ad),0.0,1.0)-pd) ) : dot(nor,pa)*dot(nor,pa)/dot2(nor) ); } ////////////////////// float GetDist(vec3 p) { float d = 0.; // Octahedron vec3 oPos; oPos = p - vec3(-3,1,7); oPos.xy *= Rotate(-u_time); // Rotates on one axis oPos.xz *= Rotate(-u_time); d = octahedronSDF(oPos,1.); // Link vec3 lPos = vec3(0,1,6); lPos = p-lPos; lPos.xz *= Rotate(-u_time); lPos.xy *= Rotate(-u_time); d = min(d,linkSDF(lPos,.2,.5,.2)); // Box vec3 bPos = p-vec3(3,1,7); bPos.xy *= Rotate(u_time); bPos.xz *= Rotate(u_time); d = min(d, roundBoxSDF(bPos,vec3(.5,.5,.5),.1)); // Plane float planeDist = p.y; d = min(d,planeDist); return d; } float RayMarch(vec3 ro, vec3 rd) { float dO = 0.; //Distane Origin for(int i=0;i<MAX_STEPS;i++) { vec3 p = ro + rd * dO; float ds = GetDist(p); // ds is Distance Scene dO += ds; if(dO > MAX_DIST || ds < SURF_DIST) break; } return dO; } vec3 GetNormal(vec3 p) { float d = GetDist(p); // Distance vec2 e = vec2(.01,0); // Epsilon vec3 n = d - vec3( GetDist(p-e.xyy), // e.xyy is the same as vec3(.01,0,0). GetDist(p-e.yxy), GetDist(p-e.yyx)); return normalize(n); } float GetLight(vec3 p) { // Directional light vec3 lightPos = vec3(5.*sin(u_time),5.,5.0*cos(u_time)); // Light Position vec3 l = normalize(lightPos-p); // Light Vector vec3 n = GetNormal(p); // Normal Vector float dif = dot(n,l); // Diffuse light dif = clamp(dif,0.,1.); // Clamp so it doesnt go below 0 // Shadows float d = RayMarch(p+n*SURF_DIST*2., l); if(d<length(lightPos-p)) dif *= .1; return dif; } void main() { vec2 uv = (gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y; vec3 ro = vec3(0,1,0); // Ray Origin/Camera vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction float d = RayMarch(ro,rd); // Distance vec3 p = ro + rd * d; float dif = GetLight(p); // Diffuse lighting vec3 color = vec3(dif); // Set the output color gl_FragColor = vec4(color,1.0); }
2. 3D Primitives Example 2

Example 2
This example shows how smooth blending and boolean subtraction can be used to create more interesting shapes from simple geometry:
precision highp float; uniform vec2 u_resolution;// Width & height of the shader uniform float u_time;// Time elapsed // Constants #define PI 3.1415925359 #define MAX_STEPS 100// Mar Raymarching steps #define MAX_DIST 100.// Max Raymarching distance #define SURF_DIST.01// Surface Distance mat2 Rotate(float a) { float s=sin(a), c=cos(a); return mat2(c,-s,s,c); } /////////////////////// // Primitives /////////////////////// // Round Box - exact float roundBoxSDF( vec3 p, vec3 b, float r ) { vec3 q = abs(p) - b; return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r; } // Triangular Prism - exact float triPrismSDF( vec3 p, vec2 h ) { const float k = sqrt(3.0); h.x *= 0.5*k; p.xy /= h.x; p.x = abs(p.x) - 1.0; p.y = p.y + 1.0/k; if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0; p.x -= clamp( p.x, -2.0, 0.0 ); float d1 = length(p.xy)*sign(-p.y)*h.x; float d2 = abs(p.z)-h.y; return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } // Rounded Cylinder - exact float roundedCylinderSDF( vec3 p, float ra, float rb, float h ) { vec2 d = vec2( length(p.xz)-2.0*ra+rb, abs(p.y) - h ); return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb; } /////////////////////// // Boolean Operators /////////////////////// float intersectSDF(float distA, float distB) { return max(distA, distB); } float unionSDF(float distA, float distB) { return min(distA, distB); } float differenceSDF(float distA, float distB) { return max(distA, -distB); } ///////////////////////////// // Smooth blending operators ///////////////////////////// float smoothIntersectSDF(float distA, float distB, float k ) { float h = clamp(0.5 - 0.5*(distA-distB)/k, 0., 1.); return mix(distA, distB, h ) + k*h*(1.-h); } float smoothUnionSDF(float distA, float distB, float k ) { float h = clamp(0.5 + 0.5*(distA-distB)/k, 0., 1.); return mix(distA, distB, h) - k*h*(1.-h); } float smoothDifferenceSDF(float distA, float distB, float k) { float h = clamp(0.5 - 0.5*(distA+distB)/k, 0., 1.); return mix(distA, -distB, h ) + k*h*(1.-h); } ///////////////////////// float GetDist(vec3 p) { float d=0.; // Circle vec3 tPos=vec3(-1,1,6); tPos=p-tPos; tPos.xz*=Rotate(-u_time); tPos.xy*=Rotate(-u_time); d=roundedCylinderSDF(tPos,.4,.05,.1); d=smoothDifferenceSDF(d,roundedCylinderSDF(tPos,.3,.2,.2),.05); // Triangle vec3 tr0Pos=p-vec3(-3,1,6); tr0Pos.xy*=Rotate(-u_time); tr0Pos.xz*=Rotate(-u_time); d=min(d,triPrismSDF(tr0Pos,vec2(1.,.1))-.05); // Subtracts -.05 from the distance in the end to give the triangle round edges d=smoothDifferenceSDF(d,triPrismSDF(tr0Pos,vec2(.7,.1 + SURF_DIST*5.)),.05); // Cross vec3 cPos=p-vec3(1,1,6); cPos.xy*=Rotate(u_time); cPos.xz*=Rotate(u_time); d=min(d,roundBoxSDF(cPos,vec3(.8,.1,.1),.05)); d=unionSDF(d,roundBoxSDF(cPos,vec3(.1,.8,.1),.05)); // Square vec3 sq0Pos=p-vec3(3,1,6); sq0Pos.xy*=Rotate(u_time); sq0Pos.xz*=Rotate(u_time); d = min(d,roundBoxSDF(sq0Pos,vec3(.7,.7,.1),.05)); d = smoothDifferenceSDF(d,roundBoxSDF(sq0Pos,vec3(.6,.6,.2),-.05),.05); // Plane float planeDist=p.y; d = smoothUnionSDF(d,planeDist,smoothstep(1.,0.,p.y)); return d; } float RayMarch(vec3 ro,vec3 rd) { float dO=0.;//Distane Origin for(int i=0;i<MAX_STEPS;i++) { vec3 p=ro+rd*dO; float ds=GetDist(p);// ds is Distance Scene dO+=ds; if(dO>MAX_DIST||ds<SURF_DIST) break; } return dO; } vec3 GetNormal(vec3 p) { float d=GetDist(p);// Distance vec2 e=vec2(.01,0);// Epsilon vec3 n=d-vec3( GetDist(p-e.xyy),// e.xyy is the same as vec3(.01,0,0). The x of e is .01. this is called a swizzle GetDist(p-e.yxy), GetDist(p-e.yyx)); return normalize(n); } float GetLight(vec3 p) { // Directional light vec3 lightPos=vec3(5.*sin(u_time),5.,6.+5.*cos(u_time));// Light Position vec3 l=normalize(lightPos-p);// Light Vector vec3 n=GetNormal(p);// Normal Vector float dif=dot(n,l);// Diffuse light dif=clamp(dif,0.,1.);// Clamp so it doesnt go below 0 // Shadows float d=RayMarch(p+n*SURF_DIST*2.,l); if(d<length(lightPos-p))dif*=.1; return dif; } void main() { vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y; vec3 ro=vec3(0,1,0);// Ray Origin/Camera vec3 rd=normalize(vec3(uv.x,uv.y,1));// Ray Direction float d=RayMarch(ro,rd);// Distance vec3 p=ro+rd*d; float dif=GetLight(p);// Diffuse lighting vec3 color=vec3(dif); // Set the output color gl_FragColor=vec4(color,1.); }
Sources
The Art of Code (Youtube): The Art Of Code
Inigo Quilez: http://www.iquilezles.org