November 24, 2014

Sphere with Occlusion

Improved on my previous sphere examples where now the little sphere can go behind the bigger sphere.

I'm not sure if I'm doing it completely right.. my shadow is kinda hacky when the little sphere goes behind the bigger sphere.  I basically check to see if the z coordinate is negative and if it is, I add shadow to it based on its z position.  If I move the spheres to a different location then it won't work anymore, so I'll need to fix that.

Again gif recording software sucks so here are two images:


To see it in action: (link to shadertoy)

And source code:
struct sphere {
    vec3 pos;
    float radius;
    vec3 col;
    int id;
};
 
vec3 light = vec3(9.0, 3.0, 8.0);
vec3 eye = vec3(0.0, 0.0, 8.0);
vec3 up = vec3(0.0, 1.0, 0.0);
sphere s1;
sphere s2;

struct eye_coord_system  {
    vec3 n;
    vec3 u;
    vec3 v;
};

float iSphere(vec3 ray_origin, vec3 ray_direction, sphere sph) {
    vec3 d = ray_direction;
    float a = dot(d, d);
    float b = 2.0 * dot(d, ray_origin - sph.pos);
    float c = dot(ray_origin - sph.pos, ray_origin - sph.pos) - (sph.radius * sph.radius);
    float delta = b*b - 4.0*a*c;
    float t = delta < 0.0 ? -1.0 : (-b - sqrt(delta)) / (2.0*a);
    return t;
}

vec3 get_sphere_normal(vec3 pos, sphere sph ) {
    return (pos - sph.pos)/sph.radius;
}

vec3 get_color_of_point(eye_coord_system eye_coord, vec3 ray_origin, vec3 ray_direction, vec3 col) {
    vec3 pos1 = vec3(-1.0), pos2 = vec3(-1.0);
    float intersection1 = iSphere(ray_origin, ray_direction, s1);
 
  if(intersection1 > 0.0) {
        pos1 = ray_origin + intersection1*ray_direction;
  vec3 nor = get_sphere_normal(pos1, s1);
        vec3 reflection = normalize(reflect(ray_direction, nor));
  float diffuse = max(0.0, dot(nor, normalize(light)));
        float specular = pow(dot(reflection, eye_coord.u), 30.0);
        specular = max(specular, 0.000001); // for some reason 0.0 returns crappy results

        float reflect_intersection = iSphere(pos1, reflection, s2);
        reflection = vec3(0.0);
     
        if (reflect_intersection > 0.0) {
            reflection = vec3(s2.col * diffuse);
        }
         
  col = vec3(diffuse + reflection + specular);
  }
 
  float intersection2 = iSphere(ray_origin, ray_direction, s2);
  if(intersection2 > 0.0) {
     pos2 = ray_origin + intersection2*ray_direction;
     vec3 nor = get_sphere_normal(pos2, s2);
        vec3 reflection = normalize(reflect(ray_direction, nor));
      float diffuse = max(0.0, dot(nor, normalize(light)));
        float specular = pow(dot(reflection, eye_coord.u), 30.0);
     specular = max(specular, 0.000001); // for some reason 0.0 returns crappy results
        if (pos2.z > 0.0 && pos2.z > pos1.z || pos2.z < 0.0 && pos1.z < 0.0) {
         col = s2.col * diffuse + specular * 0.5;
            col = pos2.z < 0.0 && pos1.z < 0.0 ? col - abs(pos2.z) : col;
     }
  }
  return col;
}

vec3 orbit(sphere s) {
    float orbit_radius = 1.8;
    return vec3 (orbit_radius*cos(iGlobalTime), 0.0, sin(iGlobalTime)*orbit_radius);
}

eye_coord_system create_coord_system(vec3 eye, vec3 center, vec3 up) {
    vec3 n = vec3(eye - center) / length(eye - center);
    vec3 u = cross(up, n) / length(cross(up, n));
    vec3 v = cross(n, u);
    eye_coord_system coord_system = eye_coord_system(n, u, v);
    return coord_system;
}

vec3 calculate_pixel_loc(sphere sph, vec3 eye, eye_coord_system eye_coord) {
    float i = gl_FragCoord.x; float j = gl_FragCoord.y;
    float X = iResolution.x; float Y = iResolution.y;
    float aspectRatio = iResolution.x / iResolution.y;
    float d = distance(sph.pos, eye)/2.0;
    float H = 1.5; float W = H * aspectRatio;
 
    vec3 C = eye - eye_coord.n * d;
    vec3 L = C - eye_coord.u * (W/2.0) - eye_coord.v * (H/2.0);
    vec3 s = L + eye_coord.u * i * (W/X) + eye_coord.v * j * (H/Y);
 
    return s;
}

void main(void) {
    vec3 s1_pos = vec3(0.0), s2_pos = vec3(orbit(s2));
    float s1_radius = 1.0, s2_radius = 0.3;
    vec3 s1_col = vec3(0.3), s2_col = vec3(1.0, 0.0, 1.0);
    int s1_id = 1, s2_id = 2;
    s1 = sphere(s1_pos, s1_radius, s1_col, s1_id);
    s2 = sphere(s2_pos, s2_radius, s2_col, s2_id);
 
    eye_coord_system eye_coord = create_coord_system(eye, s1.pos, up);
    vec3 s = calculate_pixel_loc(s1, eye, eye_coord);
    vec3 ray_direction = s - eye;
 
    vec3 col = vec3(0.3);
    col = get_color_of_point(eye_coord, eye, ray_direction, col);
 
    gl_FragColor = vec4(col,1.0);
}

I use 118 lines of code to do this while I bet iq could do it in less than 50 :( that's why he works at Pixar