this is an interesting question (and I am actually an author on Scratchapixel so I can maybe help on that one)).
Things go as follows:
- you cast the primary ray into the scene
- the ray hits the glass which is a refractive-reflective/transparent material
- you compute and cast two rays from the point of intersection: a reflective ray and a refractive ray
- if the refractive ray hits an object contained in the transparent object (the glass) then you need to shade that object too. So if it's a plastic material, compute the result of that shader (including the lighting, so loop over all lights and add their contribution, etc.)
- the color at the intersection point can finally be computed as a mix between the reflection and the refraction color (where as the mix is driven by the fresnel effect).
Now that works but this is essentially wrong.
Why? Because in fact light rays too are bent by say glass and water. So in fact when you cast a shadow ray from a point on the pen in the direction of the light, this ray direction is in fact wrong, since you should account for refraction and reflection (the ray should be bent when it goes from one medium to another in this case from water to air) but this is impossible to do with this algorithm/approach.
Though in general this gives acceptable results so no one cares much but if your goal is really a physically plausible image then this approach is wrong.
A solution to this maybe is to use photon mapping, where photons of light are cast from the light into the scene in a pre-pass. Thus when a light-photon hits the glass/water surface it will bent due to the law of refraction and eventually it will be "deposited" onto the pen. This is a bit hard to explain here but hopefully there will be a lesson on Scratchapixel about this in the future. Hope it helps.
EDIT
> Thanks for clarifying, I had one other issue though. In the source code provided on the website for that lesson there doesn't seem to be code present to spawn a ray when the light ray leaves the glass cylinder. Is this unnescessary or is it actually already present? If it is could you point out where in the code?
Hum it seems like my explanation wasn't clear if you ask this question. When the refractive ray hits the surface of the pen you need to compute the color of the pen at that intersection point (where the refractive ray intersected the pen's geometry). Now the pen is made out of wood for example which for the sake of simplicity is a diffuse material only (no specular). So in order to compute the color of the pen at the intersection point, you simply execute the traditional illumination loop:
Vec3f computePenColor(const Vec3f& objectColor, const Vec3f& intersectionPoint, normalAtIntersection) {
Vec3f shadedPointColor = 0;
// loop over all lights in the scene to cadd their contribution
for (i = 0; i < scene.lights.size(); ++i) {
Light currentLight = scene.lights[i];
// let's say this light source is a point light source
Vec3f lightDirection = currentLight.position - intersectionPoint;
// this cast a shadow ray from the shaded point to the light position
// if this ray intersects an object along the way then this point is in the shadow of this light and the function returns true
bool shadow = castShadowRay(lightDirection.length(), lightDirection.normalize());
// this point is not in the shadow of the light so add its contribution
if (!shadow) {
// the object is diffuse so apply cosine law only
shadedPointColor += max(0, normalAtIntersection.dot(lightDirection)) * currentLight->intensity * currentLight->color;
}
}
return shadedPointColor * objectColor;
}
So maybe as you can see more clearly from this example, is that when I say "cast a ray from the point of intersection to the light" I mean cast a shadow ray. This shadow ray helps to determine if the shaded point is in the shadow of that light. If not, then you can add the contribution of that light to the shaded point.
Now you can also see that the direction of this shadow ray is computed as the position of the point on the pen (the intersection point or shaded point) to the light position (we assume a point light source here for simplicity). So this is a straight line between the point on the pen and the light.
But this where things are wrong. This shoudn't be a straight line, since when the "shadow ray" (which is also a light ray but we go from the point on the pen to the light rather than from the light to the pen, but in essence they are the same thing) exits the water volume it should be bent due to refraction (and part of it should be reflected to). You can compute this new direction if you wish, but then if the shadow ray is bent when it leaves the volume of water then it won't travel in the direction of the light anymore (as showed in the pic below), so you can't use it as a shadow or light ray any longer.
So this algorithm that consists of looping over the lights and casting shadow ray can't be used if you really want to account for light ray bending due to reflection and refraction. Though as I said few people care about that because in reality, ignoring this fact, doesn't make much of a visual difference in 99% of cases. And as I pointed out, the solution to get it right if really needed is something like photon mapping where photon of lights are cast from the light into the scene into a pre-pass, which allows to follow the paths of light rays into the scene as they are being bent by refractive/reflective surfaces. But photon mapping is a rather complex algorithm to implement.
