
These ray marching shader examples show a couple of different methods of adding colors to ray marched objects.
The first example shows how instead of returning only floats for distances with the distance functions from previous examples we can also include colors into the calculations by using a vec4 data type to store a color in the xyz components and a distance in the w component of the vec4.
The most important change from previous examples is that we also have to return colors from all the boolean and smooth blending operators.
For a normal min/union operator, instead of only returning the distance from the closest object it should also return the colors from that object.
A simple way to do this is instead of using the min() and max() methods we check if the distance of object a is less than b, if so, return the distance and the color from object a, if not, return the distance and color from object b. This can be done with if/else-statements or with a ternary operator like so :vec4 unionSDF(vec4 a, vec4 b) {
return a.w < b.w? a : b;
}
The other examples show how to create colored CSG (constructive solid geometry) objects by using versions of the boolean and smooth blending intersect, union and difference methods that also include colors into their calculations.
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 and the Shader languages support for VS Code extensions installed: 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. One Color Per Object (vec4 Method)

Colored ray marched objects using vec4 data types
The easiest way to implement a ray marching shader with objects that have different colors is by using a vec4 data type to store the color and the distance to the object:
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
const vec4 GroundColor = vec4(1.0, 1.0, 1.0, 1.0);
const vec4 OctahedronColor = vec4(1,0,0,1);
const vec4 LinkColor = vec4(0,1,0,1);
const vec4 BoxColor = vec4(0,0,1,1);
float colorIntensity = 1.;
vec3 difColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a);
float 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.))+min(max(q.x,max(q.y,q.z)),0.)-r;
}
// Plane - exact
float planeSDF(vec3 p,vec4 n) {
// n must be normalized
return dot(p,n.xyz)+n.w;
}
// Link - exact
float linkSDF(vec3 p,float le,float r1,float r2) {
vec3 q=vec3(p.x,max(abs(p.y)-le,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.*p.x<m)q=p.xyz;
else if(3.*p.y<m)q=p.yzx;
else if(3.*p.z<m)q=p.zxy;
else return m*.57735027;
float k=clamp(.5*(q.z-q.y+s),0.,s);
return length(vec3(q.x,q.y-s+k,q.z-k));
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
return a.w > b.w ? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
return a.w < b.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
return a.w > -b.w? a : vec4(b.rgb,-b.w);
}
vec4 GetDist(vec3 p)
{
// Octahedron
vec3 o0p = vec3(-3,1,7); // Position
o0p = p - o0p;
o0p.xy *= Rotate(-u_time); // Rotate on one axis
o0p.xz *= Rotate(-u_time);
vec4 o0 = vec4(OctahedronColor.rgb,octahedronSDF(o0p,1.));
// Link
vec3 l0p = vec3(0,1,6);
l0p = p-l0p;
l0p.xz *= Rotate(-u_time);
l0p.xy *= Rotate(-u_time);
vec4 l0 = vec4(LinkColor.rgb,linkSDF(l0p,.2,.5,.2));
// Box
vec3 b0p = vec3(3,1,7);
b0p = p - b0p;
b0p.xy *= Rotate(u_time);
b0p.xz *= Rotate(u_time);
vec4 b0 = vec4(BoxColor.rgb,roundBoxSDF(b0p,vec3(.5,.5,.5),.1));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
// Scene
vec4 scene = vec4(0);
scene = unionSDF(l0,o0);
scene = unionSDF(scene,b0);
scene = unionSDF(scene,p0);
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 dColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
dColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * colorIntensity;
// 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,difColor);
if(d<length(lightPos-p))dif*=.1;
return color * 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,difColor);// Distance
vec3 p=ro+rd*d;
vec3 color=GetLight(p,difColor);// Diffuse lighting
// Set the output color
gl_FragColor=vec4(color,1.);
}
2.1 Colored Intersection (∩)

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 GroundColor = vec4(1);
float colorIntensity = 1.;
vec3 difColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a);
float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
// 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);
}
// Plane - exact
float planeSDF(vec3 p,vec4 n){
// n must be normalized
return dot(p,n.xyz)+n.w;
}
// Triangular Prism - exact
float triPrismSDF(vec3 p,vec2 h){
const float k=sqrt(3.);
h.x*=.5*k;
p.xy/=h.x;
p.x=abs(p.x)-1.;
p.y=p.y+1./k;
if(p.x+k*p.y>0.)p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.;
p.x-=clamp(p.x,-2.,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.))+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.*ra+rb,abs(p.y)-h);
return min(max(d.x,d.y),0.)+length(max(d,0.))-rb;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
float d = max(a.w, b.w);
return d == a.w? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
float d = min(a.w, b.w);
return d == a.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
float d = max(a.w, -b.w);
return d == a.w ? a : vec4(b.rgb,-b.w);
}
vec4 GetDist(vec3 p)
{
// Box
vec3 bSize = vec3(.8,.8,.8); //box size
vec3 bPos = vec3(0,1,0); // box position
bPos = p-bPos;
bPos.xz*=Rotate(-u_time);
bPos.xy*=Rotate(-u_time);
vec4 b0 = vec4(BoxColor.rgb,boxSDF(bPos,bSize));
// Sphere.
vec3 sPos=vec3(0,1,0);
sPos=p-sPos;
sPos.xz*=Rotate(-u_time);
sPos.xy*=Rotate(-u_time);
vec4 s0 = vec4(SphereColor.rgb,sphereSDF(sPos,1.));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
// Scene
vec4 scene = vec4(0);
// Intersect box and sphere
scene = intersectSDF(b0,s0);
// Unite boxsphere with ground
scene = unionSDF(scene,p0);
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 difColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
difColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * colorIntensity;
// Directional light
vec3 lightPos=vec3(5.*sin(u_time),5.,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,difColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,difColor);// Distance
vec3 p=ro+rd*d;
vec3 color=GetLight(p,difColor);// Diffuse lighting
// Set the output color
gl_FragColor=vec4(color,1.);
}
2.2 Colored Union (∪)

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 CylinderColor = vec4(0,0,1,1);
const vec4 GroundColor = vec4(1);
float colorIntensity = 1.;
vec3 difColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a);
float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
// 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);
}
float cappedCylinderSDF( vec3 p, float h, float r )
{
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
// Plane - exact
float planeSDF(vec3 p,vec4 n){
// n must be normalized
return dot(p,n.xyz)+n.w;
}
// Triangular Prism - exact
float triPrismSDF(vec3 p,vec2 h){
const float k=sqrt(3.);
h.x*=.5*k;
p.xy/=h.x;
p.x=abs(p.x)-1.;
p.y=p.y+1./k;
if(p.x+k*p.y>0.)p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.;
p.x-=clamp(p.x,-2.,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.))+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.*ra+rb,abs(p.y)-h);
return min(max(d.x,d.y),0.)+length(max(d,0.))-rb;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
float d = max(a.w, b.w);
return d == a.w? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
float d = min(a.w, b.w);
return d == a.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
float d = max(a.w, -b.w);
return d == a.w ? a : vec4(b.rgb,-b.w);
}
vec4 GetDist(vec3 p)
{
// Rotate the whole scene
p.xz *=Rotate(u_time / 3.0);
// Cylinders
float c0h = 1.,c0r = .8; // Cylinder height, radius.
vec3 c0p = p - vec3 (0,1,0); // Position
vec4 c0 = vec4(CylinderColor.rgb,cappedCylinderSDF(c0p,c0h,c0r));
float c1h = 1.,c1r = .8;
vec3 c1p = p - vec3 (0,1,0);
c1p.xy *= Rotate(PI*.5); // Rotate
vec4 c1 = vec4(CylinderColor.rgb,cappedCylinderSDF(c1p,c1h,c1r));
float c2h = 1.,c2r = .8;
vec3 c2p = p - vec3 (0,1,0);
c2p.xy *= Rotate(PI*.5);
c2p.yz *= Rotate(PI*.5);
vec4 c2 = vec4(CylinderColor.rgb,cappedCylinderSDF(c2p,c2h,c2r));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
vec4 scene = vec4(0);
scene = unionSDF(c0,c1);
scene = unionSDF(scene,c2);
scene = unionSDF(scene,p0);
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 difColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
difColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * colorIntensity;
// Directional light
vec3 lightPos=vec3(5.*sin(u_time),5.,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,difColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,difColor);// Distance
vec3 p=ro+rd*d;
vec3 dif=GetLight(p,difColor);// Diffuse lighting
vec3 color=vec3(dif);
// Set the output color
gl_FragColor=vec4(color,1.);
}
2.3 Colored Difference (−)

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 CylinderColor = vec4(0,0,1,1);
const vec4 GroundColor = vec4(1);
float colorIntensity = 1.;
vec3 difColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a); float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
// 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);
}
float cappedCylinderSDF( vec3 p, float h, float r ) {
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
// Plane - exact
float planeSDF(vec3 p,vec4 n) {
// n must be normalized
return dot(p,n.xyz)+n.w;
}
// Triangular Prism - exact
float triPrismSDF(vec3 p,vec2 h) {
const float k=sqrt(3.);
h.x*=.5*k;
p.xy/=h.x;
p.x=abs(p.x)-1.;
p.y=p.y+1./k;
if(p.x+k*p.y>0.)p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.;
p.x-=clamp(p.x,-2.,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.))+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.*ra+rb,abs(p.y)-h);
return min(max(d.x,d.y),0.)+length(max(d,0.))-rb;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
float d = max(a.w, b.w);
return d == a.w? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
float d = min(a.w, b.w);
return d == a.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
float d = max(a.w, -b.w);
return d == a.w ? a : vec4(b.rgb,-b.w);
}
vec4 GetDist(vec3 p)
{
// Rotate the whole scene
p.xz *=Rotate(u_time * .2);
// Box
vec3 b0s = vec3(.75,.75,.75); //box size
vec3 b0p = vec3(0,1,0); // box position
b0p = p-b0p;
vec4 b0 = vec4(BoxColor.rgb,boxSDF(b0p,b0s)); // Box Color, box distance field
// Sphere.
vec3 s0p=vec3(0,1,0);
s0p=p-s0p;
vec4 s0 = vec4(SphereColor.rgb,sphereSDF(s0p,1.05));
// Cylinders
float c0h = 1.,c0r = .55; // Cylinder height, radius.
vec3 c0p = p - vec3 (0,1,0); // Position
vec4 c0 = vec4(CylinderColor.rgb,cappedCylinderSDF(c0p,c0h,c0r));
float c1h = 1.,c1r = .55;
vec3 c1p = p - vec3 (0,1,0);
c1p.xy *= Rotate(PI*.5); // Rotate
vec4 c1 = vec4(CylinderColor.rgb,cappedCylinderSDF(c1p,c1h,c1r));
float c2h = 1.,c2r = .55;
vec3 c2p = p - vec3 (0,1,0);
c2p.xy *= Rotate(PI*.5);
c2p.yz *= Rotate(PI*.5);
vec4 c2 = vec4(CylinderColor.rgb,cappedCylinderSDF(c2p,c2h,c2r));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
vec4 scene = vec4(0), csg0 = vec4(0), csg1 = vec4(0);
csg0 = intersectSDF(b0,s0); // Intersect box with sphere creating a CSG object.
csg1 = unionSDF(c0,c1); // Unite cylinders
csg1 = unionSDF(csg1,c2);
csg0 = differenceSDF(csg0,csg1); // Subtract cylinders from boxsphere
scene = unionSDF(csg0,p0); // Use Union(min) on the CSG and the ground plane
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 dColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
dColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * colorIntensity;
// Directional light
vec3 lightPos=vec3(5.*sin(u_time),5.,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,difColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,difColor);// Distance
vec3 p=ro+rd*d;
vec3 color=GetLight(p,difColor);// Diffuse lighting
// Set the output color
gl_FragColor=vec4(color,1.);
}
3.1 Colored SmoothIntersection

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 CylinderColor = vec4(0,0,1,1);
const vec4 GroundifColor = vec4(1);
float colorIntensity = 1.;
vec3 difColor = vec3(1); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a); float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
// 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);
}
// Plane - exact
float planeSDF(vec3 p,vec4 n) {
// n must be normalized
return dot(p,n.xyz)+n.w;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
return a.w > b.w ? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
return a.w < b.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
return a.w > -b.w? a : vec4(b.rgb,-b.w);
}
/////////////////////////////
// Smooth blending operators
/////////////////////////////
vec4 smoothIntersectSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 - 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w,b.w,h) + k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothUnionSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 + 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, b.w, h) - k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothDifferenceSDF(vec4 a, vec4 b, float k)
{
float h = clamp(0.5 - 0.5*(a.w+b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, -b.w, h ) + k*h*(1.-h);
return vec4(c,d);
}
/////////////////////////
vec4 GetDist(vec3 p)
{
// Rotate the whole scene
// p.xz *=Rotate(PI*.25);
p.xz *=Rotate(u_time * .2);
// Box
vec3 bSize = vec3(.75,.75,.75); //box size
vec3 bPos = vec3(0,1,0); // box position
bPos = p-bPos;
vec4 b0 = vec4(BoxColor.rgb,boxSDF(bPos,bSize)); // Box distance field
// Sphere
vec3 sPos=vec3(0,1,0);
sPos=p-sPos;
vec4 s0 = vec4(SphereColor.rgb,sphereSDF(sPos,1.05));
// Plane
vec4 p0 = vec4(GroundifColor.rgb,planeSDF(p,vec4(0,1,0,0)));
vec4 scene = smoothIntersectSDF(b0,s0,.05); // Intersect box with sphere
scene = unionSDF(scene,p0); // Use Union(min) on the CSG and the ground plane
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 difColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
difColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * colorIntensity;
// Directional light
vec3 lightPos=vec3(-2,5,-5);// Light Position
lightPos=vec3(5.*sin(u_time),5.,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,difColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,difColor);// Distance
vec3 p=ro+rd*d;
vec3 dif=GetLight(p,difColor);// Diffuse lighting
vec3 color=vec3(dif);
// Set the output color
gl_FragColor=vec4(color,1.);
}
3.2 Colored SmoothUnion (∪)

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 CylinderColor = vec4(0,0,1,1);
const vec4 GroundColor = vec4(1);
float ColorIntensity = 1.;
vec3 dColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a); float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
float cappedCylinderSDF( vec3 p, float h, float r ) {
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
// Plane - exact
float planeSDF(vec3 p,vec4 n) {
// n must be normalized
return dot(p,n.xyz)+n.w;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
return a.w > b.w ? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
return a.w < b.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
return a.w > -b.w? a : vec4(b.rgb,-b.w);
}
/////////////////////////////
// Smooth blending operators
/////////////////////////////
vec4 smoothIntersectSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 - 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w,b.w,h) + k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothUnionSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 + 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, b.w, h) - k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothDifferenceSDF(vec4 a, vec4 b, float k)
{
float h = clamp(0.5 - 0.5*(a.w+b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, -b.w, h ) + k*h*(1.-h);
return vec4(c,d);
}
/////////////////////////
vec4 GetDist(vec3 p)
{
// Rotate the whole scene
//p.xz *=Rotate(PI * .25);
p.xz *=Rotate(u_time * .2);
// Cylinders
float c0h = 1.,c0r = .55; // Cylinder height, radius.
vec3 c0p = p - vec3 (0,1,0); // Position
vec4 c0 = vec4(CylinderColor.rgb,cappedCylinderSDF(c0p,c0h,c0r));
float c1h = 1.,c1r = .55;
vec3 c1p = p - vec3 (0,1,0);
c1p.xy *= Rotate(PI*.5); // Rotate
vec4 c1 = vec4(CylinderColor.rgb,cappedCylinderSDF(c1p,c1h,c1r));
float c2h = 1.,c2r = .55;
vec3 c2p = p - vec3 (0,1,0);
c2p.xy *= Rotate(PI*.5);
c2p.yz *= Rotate(PI*.5);
vec4 c2 = vec4(CylinderColor.rgb,cappedCylinderSDF(c2p,c2h,c2r));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
vec4 scene = smoothUnionSDF(c0,c1,.1); // Unite cylinders
scene = smoothUnionSDF(scene,c2,.1);
scene = unionSDF(scene,p0); // Use Union(min) on the CSG and the ground plane
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 dColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
dColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * ColorIntensity;
// Directional light
// vec3 lightPos=vec3(-2,5,-5);// Light Position
vec3 lightPos=vec3(5.*sin(u_time),5.,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,dColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,dColor);// Distance
vec3 p=ro+rd*d;
vec3 dif=GetLight(p,dColor);// Diffuse lighting
vec3 color=vec3(dif);
// Set the output color
gl_FragColor=vec4(color,1.);
}
3.3 Colored SmoothDifference

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
const vec4 BoxColor = vec4(1,0,0,1);
const vec4 SphereColor = vec4(0,1,0,1);
const vec4 CylinderColor = vec4(0,0,1,1);
const vec4 GroundColor = vec4(1);
float ColorIntensity = 1.;
vec3 dColor = vec3(1.0, 1.0, 1.0); // Diffuse Color
mat2 Rotate(float a) {
float s=sin(a); float c=cos(a);
return mat2(c,-s,s,c);
}
///////////////////////
// Primitives
///////////////////////
// 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);
}
float cappedCylinderSDF( vec3 p, float h, float r ) {
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
// Plane - exact
float planeSDF(vec3 p,vec4 n) {
// n must be normalized
return dot(p,n.xyz)+n.w;
}
// Triangular Prism - exact
float triPrismSDF(vec3 p,vec2 h) {
const float k=sqrt(3.);
h.x*=.5*k;
p.xy/=h.x;
p.x=abs(p.x)-1.;
p.y=p.y+1./k;
if(p.x+k*p.y>0.)p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.;
p.x-=clamp(p.x,-2.,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.))+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.*ra+rb,abs(p.y)-h);
return min(max(d.x,d.y),0.)+length(max(d,0.))-rb;
}
///////////////////////
// Boolean Operators
///////////////////////
vec4 intersectSDF(vec4 a, vec4 b) {
return a.w > b.w ? a : b;
}
vec4 unionSDF(vec4 a, vec4 b) {
return a.w < b.w? a : b;
}
vec4 differenceSDF(vec4 a, vec4 b) {
return a.w > -b.w? a : vec4(b.rgb,-b.w);
}
/////////////////////////////
// Smooth blending operators
/////////////////////////////
vec4 smoothIntersectSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 - 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w,b.w,h) + k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothUnionSDF(vec4 a, vec4 b, float k )
{
float h = clamp(0.5 + 0.5*(a.w-b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, b.w, h) - k*h*(1.-h);
return vec4(c,d);
}
vec4 smoothDifferenceSDF(vec4 a, vec4 b, float k)
{
float h = clamp(0.5 - 0.5*(a.w+b.w)/k, 0., 1.);
vec3 c = mix(a.rgb,b.rgb,h);
float d = mix(a.w, -b.w, h ) + k*h*(1.-h);
return vec4(c,d);
}
/////////////////////////
vec4 GetDist(vec3 p)
{
// Rotate the whole scene
//p.xz *=Rotate(PI * .25);
p.xz *=Rotate(u_time * .2);
// Box
vec3 b0s = vec3(.75,.75,.75); //box size
vec3 b0p = vec3(0,1,0); // box position
b0p = p-b0p;
vec4 b0 = vec4(BoxColor.rgb,boxSDF(b0p,b0s));
// Sphere.
vec3 s0p=vec3(0,1,0);
s0p=p-s0p;
vec4 s0 = vec4(SphereColor.rgb,sphereSDF(s0p,1.05));
// Cylinders
float c0h = 1.,c0r = .55; // Cylinder height, radius.
vec3 c0p = p - vec3 (0,1,0); // Position
vec4 c0 = vec4(CylinderColor.rgb,cappedCylinderSDF(c0p,c0h,c0r));
float c1h = 1.,c1r = .55;
vec3 c1p = p - vec3 (0,1,0);
c1p.xy *= Rotate(PI*.5); // Rotate
vec4 c1 = vec4(CylinderColor.rgb,cappedCylinderSDF(c1p,c1h,c1r));
float c2h = 1.,c2r = .55;
vec3 c2p = p - vec3 (0,1,0);
c2p.xy *= Rotate(PI*.5);
c2p.yz *= Rotate(PI*.5);
vec4 c2 = vec4(CylinderColor.rgb,cappedCylinderSDF(c2p,c2h,c2r));
// Plane
vec4 p0 = vec4(GroundColor.rgb,planeSDF(p,vec4(0,1,0,0)));
vec4 scene = vec4(0), csg0 = vec4(0), csg1 = vec4(0);
csg0 = smoothIntersectSDF(b0,s0,.05); // Intersect box with sphere
csg1 = smoothUnionSDF(c0,c1,.1); // Unite cylinders
csg1 = smoothUnionSDF(csg1,c2,.1);
csg0 = smoothDifferenceSDF(csg0,csg1,.05); // Subtract cylinders from boxsphere
scene = unionSDF(csg0,p0); // Use Union(min) on the CSG and the ground plane
return scene;
}
float RayMarch(vec3 ro,vec3 rd, inout vec3 dColor)
{
float dO=0.;//Distane Origin
for(int i=0;i<MAX_STEPS;i++)
{
if(dO>MAX_DIST)
break;
vec3 p=ro+rd*dO;
vec4 ds=GetDist(p);// ds is Distance Scene
if(ds.w<SURF_DIST)
{
dColor = ds.rgb;
break;
}
dO+=ds.w;
}
return dO;
}
vec3 GetNormal(vec3 p)
{
float d=GetDist(p).w;// Distance
vec2 e=vec2(.01,0);// Epsilon
vec3 n=d-vec3(
GetDist(p-e.xyy).w,// 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).w,
GetDist(p-e.yyx).w);
return normalize(n);
}
vec3 GetLight(vec3 p, vec3 c)
{
// Diffuse Color
vec3 color = c.rgb * ColorIntensity;
// Directional light
// vec3 lightPos=vec3(-2,5,-5);// Light Position
vec3 lightPos=vec3(5.*sin(u_time),5.,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,dColor);
if(d<length(lightPos-p))dif*=.1;
return color * dif;
}
void main()
{
vec2 uv=(gl_FragCoord.xy-.5*u_resolution.xy)/u_resolution.y;
vec3 ro = vec3(0,3,-3.5); // Ray Origin/Camera position
vec3 rd = normalize(vec3(uv.x,uv.y,1)); // Ray Direction
rd.zy *= Rotate(PI*-.2); // Rotate camera down on the x-axis
float d=RayMarch(ro,rd,dColor);// Distance
vec3 p=ro+rd*d;
vec3 dif=GetLight(p,dColor);// Diffuse lighting
vec3 color=vec3(dif);
// Set the output color
gl_FragColor=vec4(color,1.);
}
Sources
Wikipedia: https://en.wikipedia.org/wiki/Constructive_solid_geometry
The Art of Code (Youtube): The Art Of Code
Inigo Quilez: http://www.iquilezles.org