Skip to content

Instantly share code, notes, and snippets.

@zellyn
Created December 9, 2015 12:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zellyn/d9d52dad24c4f7cb009a to your computer and use it in GitHub Desktop.
Save zellyn/d9d52dad24c4f7cb009a to your computer and use it in GitHub Desktop.
Playing with distance fields on shadertoy
const int MAX_ITER = 100;
const float MAX_DIST = 20.0;
const float EPSILON = 0.001;
struct RayResult {
vec3 pos;
vec3 normal;
float dist;
float mindist;
float totaldist;
};
float sphere(vec3 p, float radius) {
return length(p) - radius;
}
float grid(vec3 p) {
p.x = mod(p.x + 0.5, 1.0) - 0.5;
p.y = mod(p.y + 0.5, 1.0) - 0.5;
return sphere(p, 0.1);
}
float oldDistFunc(vec3 p) {
float g = grid(p);
float smaller = min(
min(sphere(p-vec3(1.0, 0.0, 0.0), 0.5),
sphere(p-vec3(0.0, 1.0, 0.0), 0.5)),
min(sphere(p-vec3(-1.0, 0.0, 0.0), 0.5),
sphere(p-vec3(0.0, -1.0, 0.0), 0.5)));
float spheres = min(g, min(sphere(p, 0.3), smaller));
float floor = p.z + 1.0;
return min(spheres, floor);
}
// start iq primitives
// http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
float udRoundBox( vec3 p, vec3 b, float r )
{
return length(max(abs(p)-b,0.0))-r;
}
float sdCylinder( vec3 p, vec3 c )
{
return length(p.xz-c.xy)-c.z;
}
// end iq primitives
float distFunc(vec3 p) {
float ground = p.z;
float box = udRoundBox(p, vec3(5.0, 0.3, 5.0), 1.0);
float cyl = sdCylinder(p, vec3(0.0, 2.0, 2.5));
float holeyBox = max(box, -cyl);
// return min(oldDistFunc(p), min(holeyBox, ground));
return min(holeyBox, ground);
}
vec3 getNormal(vec3 pos){
vec2 eps = vec2(0.0, EPSILON);
return normalize(vec3(
distFunc(pos + eps.yxx) - distFunc(pos - eps.yxx),
distFunc(pos + eps.xyx) - distFunc(pos - eps.xyx),
distFunc(pos + eps.xxy) - distFunc(pos - eps.xxy)));
}
float softShadow(in vec3 ro, in vec3 rd, float mint, float maxt, float k)
{
float res = 1.0;
float t = mint;
for( int i=0; i < 100; i++)
{
float h = distFunc(ro + rd*t);
res = min(res, max(k*h/t, 0.0));
t += clamp( h, 0.02, 0.3 );
if( h<0.001 || t>maxt ) continue;
}
return res;
}
RayResult castRay(vec3 rPos, vec3 rDir){
RayResult result;
result.dist=MAX_DIST;
result.mindist=MAX_DIST;
result.totaldist=0.0;
for (int i = 0; i < MAX_ITER; i++)
{
if (result.dist < result.mindist) { result.mindist = result.dist;}
if (result.dist < EPSILON || result.dist > MAX_DIST) {continue;}
result.dist = distFunc(rPos); // Evalulate the distance at the current point
result.totaldist += result.dist;
rPos += result.dist * rDir; // Advance the point forwards in the ray direction by the distance
}
result.pos = rPos;
result.normal = getNormal(result.pos);
return result;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Copied from
// http://www.reddit.com/r/twotriangles/comments/1hy5qy/tutorial_1_writing_a_simple_distance_field/
// and https://www.shadertoy.com/view/MsSGW1
float viewPlaneDistance = 1.5;
vec3 cameraOrigin = vec3(-12.0,
-9.0,
6.0);
vec3 cameraWiggle = vec3(0.4 * sin(iGlobalTime/13.0),
0.0,
1.3 * sin(iGlobalTime/7.0));
cameraOrigin += cameraWiggle;
vec3 cameraTarget = vec3(0.0, 0.0, 2.0);
vec3 upDirection = vec3(0.0, 0.0, 1.0);
vec3 cameraDir = normalize(cameraTarget - cameraOrigin);
vec3 cameraRight = normalize(cross(upDirection, cameraOrigin));
vec3 cameraUp = cross(-cameraDir, cameraRight);
vec3 lightPos = vec3(2.0, -12.0, 9.0);
vec3 lightWiggle = vec3(1.0*sin(iGlobalTime/101.0),
3.0*cos(iGlobalTime/3.0),
6.0*sin(iGlobalTime/3.0));
lightPos += lightWiggle;
// it's a lot easier to work with if we rescale to be between -1 and 1 and correct
// the aspect ratio:
vec2 screenPos = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy; // screenPos can range from -1 to 1
screenPos.x *= iResolution.x / iResolution.y; // Correct aspect ratio
vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + viewPlaneDistance * cameraDir);
RayResult ray = castRay(cameraOrigin,rayDir);
vec3 c;
if(ray.dist<EPSILON){
float diffuse = max(0.1, dot(normalize(lightPos-ray.pos), ray.normal));
diffuse *= softShadow(ray.pos, normalize(lightPos-ray.pos), 0.02, 0.5, 8.0);
float specular = pow(diffuse, 32.0);
c = vec3(diffuse + specular);
} else {
c = vec3(0.0);
}
fragColor = vec4(c, 1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment