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);
}
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