Ray Marching Shader pt.5 (Colors)

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

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.