At the request of a friend from work, I put together a little something with some “light and shadow“. The trick to basic shadows when working with ray casting is this:

- Do a normal ray cast and find your ray intersection with the scene.
- From that intersection point, try to ray cast toward the light. If an intersection is encountered, the point is in shadow and should be shaded appropriately.

In code, this is pretty straightforward. From tonight’s shader:

//intersect the ray with scene vec2 t; vec4 sphHit; float id = intersect(rayOrigin, rayDirection, t, sphHit); |

This is the initial intersection with the scene. We send out a ray from our origin point with a direction, and in return we get a t (ray length) and sphere (defined by a vec4) as well as an identifier for which sphere was hit. If nothing is hit, we’ll get -1.0 for this identifier.

//If we hit a sphere if(id >= 0.0) { //find the point where we hit the sphere and evaluate luminance vec3 pos = rayOrigin + t.x*rayDirection; vec3 nor = nSphere(pos, sphHit); float dif = clamp(dot(nor, normalize(light-pos)), 0.0, 1.0); col = vec4(vec3(dif), 1.0); |

If we determine that we’ve hit something, we’ll figure out exactly where we hit it and calculate Lambertian reflectance for that point.

//check to see if this point is in shadow vec2 shadowT; vec4 shadowHit; //check for intersect between the sphere and the light float shadowId = intersect(pos, normalize(light-pos), shadowT, shadowHit); //if we have a non-negative id, we've hit something other than the light if (shadowId >= 0.0) { col = vec4(0.0); } } |

Then, we try to intersect the scene from our original intersection point with a ray in the direction toward the light. If we hit something, our point is in shadow, so we’ll replace the color with black, which will give us a hard shadow.

//If we hit the light else if (id == -2.0) { col = vec4(1.0); } |

Finally, we’ll add the light to the scene as its own special case, making it totally white. And that’s it!