Snippets

Joseph Chow Shader Template

Created by Joseph Chow last modified
// time is a global variable. Re-defining so it's a little less to type
#define time iGlobalTime

// define the maximum number of times we raymarch.
#define MAX_STEPS 100

// defines how close we can get before we've hit the scene
#define HOW_CLOSE 0.001

// defines how far the ray can go
#define FURTHEST_OUR_RAY_CAN_REACH 10.0

/**
 *    Defines a basic template for doing ray marching based on articles from 
 *    @cabibbo,@iq and @nicoptere
 *    
 *    The variables defined and functions are for ShaderToy / OpenGL ES 2 but should be fairly
 *    portable to another context. 
 *  
 *    This particular template starts off by drawing a yellow-orange sphere
 * 
 *    http://barradeau.com/blog/?p=575
 *    https://www.shadertoy.com/view/Xds3zN
 *    https://www.shadertoy.com/view/Xl2XWt
 */

/////////// UTILITY FUNCTIONS  ///////////////
// This calculation basically gets a way for us to 
// transform the rays coming out of our eyes and going through the window.
// aka - creating a camera
mat3 calculateEyeRayTransformationMatrix( in vec3 ro, in vec3 ta, in float roll )
{
    vec3 ww = normalize( ta - ro );
    vec3 uu = normalize( cross(ww,vec3(sin(roll),cos(roll),0.0) ) );
    vec3 vv = normalize( cross(uu,ww));
    return mat3( uu, vv, ww );
}

/////////// SHAPE FUNCTIONS ///////////////

//the signed-distance function to build a sphere
float sdSphere( vec3 p, float s )
{
    return length(p)-s;
}

vec2 sdfBalloon( vec3 currentRayPosition ){
  
  // First we define our balloon position
  vec3 balloonPosition = vec3( 0.0 , 0.0 , -0.4 );
    
  // than we define our balloon radius
  float balloonRadius = 0.5;
    
  // Here we get the distance to the surface of the balloon
  float distanceToBalloon = length( currentRayPosition - balloonPosition );
    
  // finally we get the distance to the balloon surface
  // by substacting the balloon radius. This means that if
  // the distance to the balloon is less than the balloon radius
  // the value we get will be negative! giving us the 'Signed' in
  // Signed Distance Field!
  float distanceToBalloonSurface = distanceToBalloon - balloonRadius;
    
  
  // Finally we build the full balloon information, by giving it an ID
  float balloonID = 1.;
    	
  // And there we have it! A fully described balloon!
  vec2 balloon = vec2( distanceToBalloonSurface,  balloonID );
    
  return balloon;
    
}


/////////// MAP ALL THE THINGS ///////////////


vec2 map(vec3 currentPos){
 
    vec2 result;
    
    vec2 balloon = sdfBalloon( currentPos );

    result = balloon;
    return result;
}


vec3 march(in vec3 ro, in vec3 rd ){
    vec3 result = vec3(0.);
	
    float min = HOW_CLOSE;
    float max = FURTHEST_OUR_RAY_CAN_REACH;
  
    float finalDistanceTraveledByRay = min;
    float finalID = -1.0;
    
    for( int i = 0; i < MAX_STEPS; i++ ){
      
   
        //march something!
        vec2 res = map( ro + rd * finalDistanceTraveledByRay );
        
        //if we're less than 0, thats too far, so break
        if(res.x < min || finalDistanceTraveledByRay > max){
        	break;
        }
        
        finalDistanceTraveledByRay += res.x;
        finalID = res.y;
        
    }
    
   
    if(finalDistanceTraveledByRay > max){
     	finalID = -1.0;   
    }
    
    result.x = finalDistanceTraveledByRay;
    result.y = finalID;
    return result;
}

// Calculates the normal direction so that we ca
vec3 getNormalOfSurface( in vec3 positionOfHit ){
    
		vec3 tinyChangeX = vec3( 0.001, 0.0, 0.0 );
    vec3 tinyChangeY = vec3( 0.0 , 0.001 , 0.0 );
    vec3 tinyChangeZ = vec3( 0.0 , 0.0 , 0.001 );
    
   	float upTinyChangeInX   = map( positionOfHit + tinyChangeX ).x; 
    float downTinyChangeInX = map( positionOfHit - tinyChangeX ).x; 
    
    float tinyChangeInX = upTinyChangeInX - downTinyChangeInX;
    
    
    float upTinyChangeInY   = map( positionOfHit + tinyChangeY ).x; 
    float downTinyChangeInY = map( positionOfHit - tinyChangeY ).x; 
    
    float tinyChangeInY = upTinyChangeInY - downTinyChangeInY;
    
    
    float upTinyChangeInZ   = map( positionOfHit + tinyChangeZ ).x; 
    float downTinyChangeInZ = map( positionOfHit - tinyChangeZ ).x; 
    
    float tinyChangeInZ = upTinyChangeInZ - downTinyChangeInZ;
    
    
	vec3 normal = vec3(
         			tinyChangeInX,
        			tinyChangeInY,
        			tinyChangeInZ
    	 		  );
    
	return normalize(normal);
}

// Color some objects
vec3 colorTheWorld( in vec2 rayHitInfo , in vec3 eyePosition , in vec3 rayDirection ){
 	vec3 color = vec3(0.);
    
    if(rayHitInfo.y < 0.0){
     	color = vec3(0.0,0.0,0.0);   
    }else {
        vec3 positionOfHit = eyePosition + rayHitInfo.x * rayDirection;
        vec3 normalOfSurface = getNormalOfSurface( positionOfHit );
      	
        if(rayHitInfo.y == 1.0){
           vec3 sunPosition = vec3( 1. , 4. , 3. );
    
            // the direction of the light goes from the sun
            // to the position of the hit
    		vec3 lightDirection = sunPosition - positionOfHit;
   	
    
            // Here we are 'normalizing' the light direction
            // because we don't care how long it is, we
            // only care what direction it is!
            lightDirection = normalize( lightDirection );


            // getting the value of how much the surface
            // faces the light direction
            float faceValue = dot( lightDirection , normalOfSurface );

            // if the face value is negative, just make it 0.
            // so it doesn't give back negative light values
            // cuz that doesn't really make sense...
            faceValue = max( 0. , faceValue );

            vec3 balloonColor = vec3( 1. , 1. , 0. );

            // our final color is the balloon color multiplied
            // by how much the surface faces the light
            color = balloonColor * faceValue;

            // add in a bit of ambient color
            // just so we don't get any pure black
            color += vec3( .3 , .1, .2 ); 
        }
    }
    
    
    return color;
}

/////////// RUN ALL THE THINGS ///////////////
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 currentPixel = ( -iResolution.xy + 2.0 * fragCoord.xy ) / iResolution.y;
     
    // We use the eye position to tell use where the viewer is
    vec3 eyePosition = vec3( 0., 0., 2.);
    
    // This is the point the view is looking at. 
    // The window will be placed between the eye, and the 
    // position the eye is looking at!
    vec3 pointWeAreLookingAt = vec3( 0. , 0. , 0. );
    
    
    //build the camera
    mat3 cam = calculateEyeRayTransformationMatrix(eyePosition,pointWeAreLookingAt,0.0);
     
   	vec3 rayComingOutOfEyeDirection = normalize( cam * vec3( currentPixel.xy , 2. ) ); 
  	vec3 rayHitInfo = march( eyePosition , rayComingOutOfEyeDirection );
  	  
   	vec3 t = colorTheWorld(rayHitInfo.xy,eyePosition,rayComingOutOfEyeDirection);
    
    
    fragColor = vec4(t,1.0);
	//fragColor = vec4(currentPixel,0.5+0.5*sin(iGlobalTime),1.0);
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.