NOTE: This site has just upgraded to Forester 5.x and is still having some style and functionality issues, we will fix them ASAP.

test Keenan Crane's style for rendering edge and shade [uts-001D]

Adapted from :

#define EDGE_WIDTH 0.12
#define RAYMARCH_ITERATIONS 35
#define SHADOW_ITERATIONS 40
#define SHADOW_STEP 1.0
#define SHADOW_SMOOTHNESS 256.0
#define SHADOW_DARKNESS 0.75

// Distance functions from iquilezles.org
float fSubtraction(float a, float b) {return max(-a,b);}
float fIntersection(float d1, float d2) {return max(d1,d2);}
void fUnion(inout float d1, float d2) {d1 = min(d1,d2);}
float pSphere(vec3 p, float s) {return length(p)-s;}
float pRoundBox(vec3 p, vec3 b, float r) {return length(max(abs(p)-b,0.0))-r;}
float pTorus(vec3 p, vec2 t) {vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y;}
float pTorus2(vec3 p, vec2 t) {vec2 q = vec2(length(p.xy)-t.x,p.z); return length(q)-t.y;}
float pCapsule(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;}

float mySDF(vec3 p) {
  float x = p.x;
  float y = p.y;
  float z = p.z;

  float x2 = pow(x, 2.);
  float y2 = pow(y, 2.);
  float z2 = pow(z, 2.);

  float x3 = pow(x, 3.);
  float y3 = pow(y, 3.);
  float z3 = pow(z, 3.);

  float x4 = pow(x, 4.);
  float y4 = pow(y, 4.);
  float z4 = pow(z, 4.);

  // return (y-x2)*(z-x3);

  // return 5.*(z2+y3-y4-x2*y2);

  // return (y2-x2-z2);

  return (x2-z2*y2+y3);
}

float distf(vec3 p)
{
  float d = 100000.0;

  fUnion(d, pRoundBox(vec3(0,0,10) + p, vec3(21,21,1), 1.0));
  fUnion(d, pSphere(vec3(10,10,0) + p, 8.0));
  fUnion(d, pSphere(vec3(16,0,4) + p, 4.0));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(15,15,-6.5), 1.5));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(5,15,-6.5), 1.5));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(10,5,-6.5), 1.5));
  fUnion(d, pTorus(vec3(15,-15,0) + p, vec2(6,2)));
  fUnion(d, pTorus2(vec3(10,-15,0) + p, vec2(6,2)));
  fUnion(d, pRoundBox(vec3(-10,10,-2) + p, vec3(1,1,9), 1.0));
  fUnion(d, pRoundBox(vec3(-10,10,-4) + p, vec3(0.5,6,0.5), 1.0));
  fUnion(d, pRoundBox(vec3(-10,10,2) + p, vec3(6,0.5,0.5), 1.0));

  // d = mySDF(p);
  // d = fIntersection(d, pSphere(p,15.0));

  return d;
}


vec3 grad(vec3 p)
{
  const float eps = 0.01;
  float m;
    vec3 d_distf = vec3( (distf(vec3(p.x-eps,p.y,p.z)) - distf(vec3(p.x+eps,p.y,p.z))),
                   (distf(vec3(p.x,p.y-eps,p.z)) - distf(vec3(p.x,p.y+eps,p.z))),
                   (distf(vec3(p.x,p.y,p.z-eps)) - distf(vec3(p.x,p.y,p.z+eps)))
         );
    return d_distf / (2.*eps);
}

vec3 grad2(vec3 p)
{
  const float eps = 0.01;
  float m;
    vec3 d_grad = vec3( (grad(vec3(p.x-eps,p.y,p.z)) - grad(vec3(p.x+eps,p.y,p.z))).x,
                   (grad(vec3(p.x,p.y-eps,p.z)) - grad(vec3(p.x,p.y+eps,p.z))).y,
                   (grad(vec3(p.x,p.y,p.z-eps)) - grad(vec3(p.x,p.y,p.z+eps)).z)
         );
    return d_grad / (2.*eps);
}

vec3 normal(vec3 p)
{
    return normalize(grad(p));
}


vec4 raymarch(vec3 from, vec3 increment)
{
  const float maxDist = 200.0;
  const float minDist = 0.001;
  const int maxIter = RAYMARCH_ITERATIONS;

  float dist = 0.0;

  float lastDistEval = 1e10;
  float edge = 0.0;

  for(int i = 0; i < maxIter; i++) {
    vec3 pos = (from + increment * dist);
    float distEval = distf(pos);

    if (lastDistEval < EDGE_WIDTH && distEval > lastDistEval + 0.001) {
      edge = 1.0;
    }


    if (distEval < minDist) {
      break;
    }

    dist += distEval;
    if (distEval < lastDistEval) lastDistEval = distEval;
  }

  float mat = 1.0;
  if (dist >= maxDist) mat = 0.0;

  return vec4(dist, mat, edge, 0);
}

float shadow(vec3 from, vec3 increment)
{
  const float minDist = 1.0;

  float res = 1.0;
  float t = 1.0;
  for(int i = 0; i < SHADOW_ITERATIONS; i++) {
    float h = distf(from + increment * t);
    if(h < minDist)
      return 0.0;

      res = min(res, SHADOW_SMOOTHNESS * h / t);
      t += SHADOW_STEP;
    }
    return res;
}

float rand(float x)
{
  return fract(sin(x) * 43758.5453);
}

float triangle(float x)
{
  return abs(1.0 - mod(abs(x), 2.0)) * 2.0 - 1.0;
}

// Camera localized normal
vec3 campos, camup;
vec3 localNormal(vec3 p, vec3 rd) {
    vec3 n = normal(p), ln;
    vec3 side = cross(campos, camup);
    return vec3(dot(n,  side), dot(n,  camup), dot(n,  -rd));
}

float time;
vec4 getPixel(vec2 p, vec3 from, vec3 increment, vec3 light)
{
  vec4 c = raymarch(from, increment);
  vec3 hitPos = from + increment * c.x;
  vec3 normalDir = normal(hitPos);


  float diffuse = 1.0 + min(0.0, dot(normalDir, -light));
  float inshadow = (1.0 - shadow(hitPos, -light)) * SHADOW_DARKNESS;

  // diffuse = max(diffuse, inshadow);

  // if it's not edge, and no contact
  if (c.z != 1.0 && c.y == 0.0) return vec4(0.96, 0.94, 0.87,1);

  float low = 0.05;
  float high = 0.95;

  diffuse = diffuse > high ? 1.0 : (diffuse > low ? low : diffuse);

  vec4 mCol = mix(vec4(vec3(0.78, 0.76, 0.78) * 1.15,1), vec4(0.70, 0.68, 0.75,1), diffuse);

  float gridStep = 1.0;

  // optional chess style grid
  // mCol = mix(mCol,vec4(0.,0.,0.,1.0),0.1*mod(floor(hitPos.x/gridStep)+floor(hitPos.y/gridStep)+floor(hitPos.z/gridStep),2.));

  float dt = dot(vec3(0., 0., 1.), normalDir);

  float eps_high = 0.02;
  float eps_low = 0.00000; //1;

  // vec3 n = vec4(normalDir, 0.).xyz;
  vec3 n = localNormal(hitPos, increment); // grad(hitPos); // localNormal(hitPos, increment);
  vec3 ez = vec3(0.,0.,0.1);

  // https://www.shadertoy.com/view/fsGXzc

  const float MAX_DIST = 200.0;
  float depth = distance(from, hitPos); // /MAX_DIST;

    // I've mostly just copied and pasted Evan's code.
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Compute curvature
	vec3 dx = dFdx(n);
	vec3 dy = dFdy(n);
	vec3 xneg = n - dx;
	vec3 xpos = n + dx;
	vec3 yneg = n - dy;
	vec3 ypos = n + dy;
	float curvature = (cross(xneg, xpos).y - cross(yneg, ypos).x) * 4.0 / (depth);

  // curvature debug
  // return vec4(vec3(1,0.9,0.8) * 0.7 * (abs(curvature) * 500.),1);
  ;
  vec4 curve_color = vec4(mix(vec3(1,0.9,0.8) * 0.5, vec3(1.), 0.5), 1.); // * length(grad(hitPos).xy) //  * abs(curvature) * 200.

  float curvature_eps = 0.0001;
  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).z) > eps_low && ( length(grad(hitPos).z) < eps_high * abs(curvature) * 100. ) ) {
      return curve_color;
  }

  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).y) > eps_low && ( length(grad(hitPos).y) < eps_high * abs(curvature) * 100.) ) {
      return curve_color;
  }

  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).x) > eps_low && ( length(grad(hitPos).x) < eps_high * abs(curvature) * 100.) ) {
      return curve_color;
  }

  if(c.z != 1. && length(grad(hitPos).z) > eps_low && ( length(grad(hitPos).z) < eps_high) ) {
      // return vec4(mix(vec3(1,0.9,0.8) * 0.5, vec3(1.), 0.5), 1.); // * (abs(curvature) * 1000.),1);
  }

  // .z is edge
  mCol = mix(mCol,vec4(vec3(1,0.9,0.8) * 0.5,1),c.z);

  return mCol;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  time = floor(iTime * 16.0) / 16.0;
  // pixel position
  vec2 q = fragCoord.xy / iResolution.xy;
  vec2 p = -1.0+2.0*q;
  p.x *= -iResolution.x/iResolution.y;

  // mouse
  vec2 mo = iMouse.xy/iResolution.xy;
  vec2 m = iMouse.xy / iResolution.xy;
  if (iMouse.x == 0.0 && iMouse.y == 0.0) {
    m = vec2(time * 0.06 + 1.67, 0.78);
  }
  m = -1.0 + 2.0 * m;
  m *= vec2(4.0,-0.75);
  m.y += 0.75;

  // camera position
  float dist = 65.0;
  vec3 ta = vec3(0,0,0);
  vec3 ro = vec3(cos(m.x) * cos(m.y) * dist, sin(m.x) * cos(m.y) * dist, sin(m.y) * dist);
  vec3 light = vec3(cos(m.x - 2.27) * 50.0, sin(m.x - 2.27) * 50.0, -20.0);

  // camera direction
  vec3 cw = normalize( ta-ro );
  vec3 cp = vec3( 0.0, 0.0, 1.0 );
  vec3 cu = normalize( cross(cw,cp) );
  vec3 cv = normalize( cross(cu,cw) );
  vec3 rd = normalize( p.x*cu + p.y*cv + 2.5*cw );
  campos = -cw, camup = cv;

  // calculate color
  vec4 col = getPixel(p, ro, rd, normalize(light));
  col = pow(col, vec4(1.0 / 2.2));
  col = col * 1.8 - 0.8;
  fragColor = col;
}

Color extracted with the help of .

See also:

- toon shader
- GLSL Fragment Shader: Sobel Edge Detection
- Cross hatching WebGL shader
- Penumbra Maps: Approximate Soft Shadows in Real-Time
- stackgl/glsl-lighting-walkthrough