The simplest way to add hard shadows (for point lights) is to spawn a ray from every hit point to the light and check if this ray intercepts any object in between.

This way, even if the material is transmissive, as the middle sphere in the image above, none of the light goes through.

In the code above, world is a list to all Hitable (geometric) objects in my scene. The ray r starts on the hit point and point towards the point light instance.

So, when the ray finds any obstruction Hitable::hit(…), it returns true.

Transmissivity

We can allow a percentage of light to pass through a material by creating a transmissivity index with values in range [0,1].

Now we have a more convincing portrait of reality. However, the material’s refraction index doesn’t play a role on how the light goes through when checking shadows.

To achieve that, I added a float transmissivity to my Dielectric : Material class and a non-pure virtual method Material::transmit(), returning false to all subclasses but Dielectric.

Scattering shadow rays

In order to make refraction relevant when adding shadows, it is necessary to scatter rays that hit transmissive materials.

When the ray leaving the hit point in the light’s direction finds the sphere the first time, it is refracted. This refraction creates new ray forming an angle α with the original one.

The larger the value of α is, less are the chances that the ray leaving the sphere will hit the light source.

So for a small α, and a large dot product between the original ray and the scattered (refracted) ray, it is more likely that the hit point is lit by the light, creating no shadows.

We must also check if the scattered ray is not reflected, this can be done by checking if the dot product is not negative.

In the code above, for every transmitted ray it’s made a recursive call to PointLight::inShadow(…) with the new ray being the scattered one.