October 27, 2014

Ray traced Sphere with Reflection

So I kinda hacked the reflection but it looks right:

How do I gif rid of the annoying "created with..."
link on shadertoy

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, sphere sph, vec3 col) {
    float intersection = iSphere(ray_origin, ray_direction, sph);
    if(intersection > 0.0) {
        vec3 pos = ray_origin + intersection*ray_direction;
        vec3 nor = get_sphere_normal(pos, sph);
        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 (sph.id == 1) {
            float reflect_intersection = iSphere(pos, reflection, s2);
            vec3 reflection = vec3(0.0);
            if (reflect_intersection > 0.0) {
                reflection = vec3(s2.col * diffuse);
            }
         
            col = vec3(diffuse + reflection + specular);
        }
     
        else if (sph.id == 2) {
            col = s2.col * diffuse + specular*0.5;
        }
     
    }
 
    return col;
}

vec2 dance_little_sphere() {
    return vec2 (cos(iGlobalTime), sin(iGlobalTime));
}

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) {
    s1 = sphere(vec3(0.0), 1.0, vec3(0.3), 1);
    s2 = sphere(vec3(dance_little_sphere(), 0.9), 0.3, vec3(1.0, 0.0, 1.0), 2);
 
    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, s1, col);
    col = get_color_of_point(eye_coord, eye, ray_direction, s2, col);
 
    gl_FragColor = vec4(col,1.0);
}

Basically, if we've hit the big sphere, we send another ray into space and see if it hits anything else.  If it hits the second sphere, we color that pixel the color of the second sphere.