private double ComputeLightIntensity(ILight light, Vector3d intersectionPoint, Vector3d normal, Vector3d viewDirection, double specularExponent)
        {
            if (light is AmbientLight)
            {
                return(light.Intensity);
            }

            Vector3d oppositeLightDirection;
            double   maxDistance;

            switch (light)
            {
            case DirectionalLight directionalLight:
                oppositeLightDirection = -directionalLight.Direction;
                maxDistance            = double.PositiveInfinity;
                break;

            case PointLight pointLight:
                oppositeLightDirection = pointLight.Position - intersectionPoint;
                maxDistance            = 1d;
                break;

            default:
                throw new NotSupportedException($"Cannot handle light of type {light.GetType().Name}");
            }

            // shadow
            Ray  shadowRay = new Ray(intersectionPoint, oppositeLightDirection);
            bool hasShadow = shadowRay.HasIntersection(_scene.Objects, RayTracer.Delta, maxDistance);

            if (hasShadow)
            {
                return(0d);
            }

            double intensity = 0d;

            // diffuse
            double nl = normal * oppositeLightDirection;

            if (nl > 0d)
            {
                intensity += light.Intensity * nl / (normal.Length * intersectionPoint.Length);
            }

            // specular
            if (specularExponent < 0d)
            {
                return(intensity);
            }

            Vector3d lightReflection = oppositeLightDirection.Reflect(normal);
            double   rv = lightReflection * viewDirection;

            if (rv > 0d)
            {
                intensity += light.Intensity * Math.Pow(rv / (lightReflection.Length * viewDirection.Length), specularExponent);
            }

            return(intensity);
        }