Dielectrics, rather than the definition in circuits, is material that reflects and refracts light. The most common example is glass. We have already implemented reflection, and now we are going to implement refraction.
Snell’s law and refraction
When calculating refracted ray, we use Snell’s law. Snell’s law stated that: given two medium with refractive index η and η′, a ray with incidence angle \theta and refraction angle \theta' will satisfy the following equation:
ηsin(θ)=η′sin(θ′)
The diagram below shows the refraction of light from air to glass(assumed to have refractive index of 1.5). I is the incident ray, and R is the refracted ray. We can derive the vector formula of R with I and the normal vector n of the surface.
Since we only need the direction of the refracted ray, we can treat the incident ray and the normal vector as unit vectors. By simple trig, we have:
And we can derive the Ry by using Pythagorean theorem:
Ry=−1−∣∣R∣∣x2⋅N
This is good, but cos for θ is not easy to calculate. We can use the following formula to calculate cosθ if theta is the angle between two normalized vectors A and B:
intmain(){ auto camera = Camera(1920, 16.0 / 9.0, 2.0, 1, MathUtil::Point3(0, 0, 1)); auto world = HittableList(); auto center_ball_material = std::make_shared<Dielectric>(Dielectric(1.5, ImageUtil::Color(1.0, 1.0, 1.0))); auto right_ball_material = std::make_shared<Metal>(Metal(ImageUtil::Color(0.7, 0.3, 0.3), 0.4)); auto left_ball_material = std::make_shared<Metal>(Metal(ImageUtil::Color(0.8, 0.8, 0.8), 0.4)); auto ground_material = std::make_shared<Metal>(Metal(ImageUtil::Color(0.8, 0.6, 0.2), 0.4)); world.add(std::make_shared<Sphere>(Sphere(1000, {0, -1000.5, -1.0}, ground_material))); world.add(std::make_shared<Sphere>(Sphere(0.5, {0, 0, -1}, center_ball_material))); world.add(std::make_shared<Sphere>(Sphere(0.5, {-1, 0, -1.0}, left_ball_material))); world.add(std::make_shared<Sphere>(Sphere(0.5, {1, 0, -1.0}, right_ball_material))); camera.setSampleCount(100); camera.setRenderDepth(50); camera.setRenderThreadCount(24); camera.Render(world, "out", "test.ppm"); return0; }
The result is:
Full reflection
Consider the equation deduced from Snell’s law:
sinθη′η=sinθ′
When η>η′, for some value of θ, the right-hand side will be greater than 1, and there will be no real solution for θ′. This means that the light will be fully reflected.
Since sin takes too much time to calculate, we can use our old friend, the dot product, to calculate sinθ:
Normally, real glass becomes more reflective as the angle of incidence increases. This is called Fresnel effect. Schlick’s approximation is a simple way to approximate the reflectance of a dielectric material. The formula is:
Notice the edge of the glass sphere. The reflection is stronger than the center and you can see the reflection of the other spheres.
Zooming in
Right now we have the ability to position our camera, but not yet to rotate or change the zoom. Let’s implement the second feature first, and it can be implemented by adjusting the field of view.
Field of view is the max angle of the ray that the camera can see. Give a FOV and the aspect ratio, we can calculate the height and width of the viewport. The formula is: