7 Metal and Dieletric Materials
In this chapter, we’ll quickly add two synthetic materials: metal and dielectric, so our scene could be more interesting and we can see the improvements of tiled rendering.
7.1 How Metal and Dieletric Materials Works
We follows the way of Ray Tracing in One Weekend. See it’s chapter 10 and 11 for explanations.
7.2 Design
They’re two more material types, so we just add two material variants. They should implement all functions for material we’ve defined in Chapter 4. This work is actually very trivial to implement, since we’ll use almost the same algorithm described in the above reference materials.
7.3 Imaplentation
We first add a new Metal
variant to Material
, with a new implementation struct. Same as Diffuse
, the albedo
is again a Texture
, and the fuzz
component is just a f32
value:
The scatter method is same as Shirley’s one:
impl Surface for Metal {
fn scatter(&self, ray_in: Ray, rec: &SurfaceHit) -> Option<SurfaceInteraction> {
let ref_dir =
ray_in.direction.reflect(rec.normal).normalize() + self.fuzz * Vec3::random_on_sphere();
let reflected = Ray::new(rec.position, ref_dir);
let attenuation = self.albedo.value(rec.position);
Some(SurfaceInteraction {
attenuation,
scattered: reflected,
})
}
}
Note a new reflect
method is added to Vec3Ext
, for calculating a reflected ray. And the Dielectric
is also almost same, so you can translate the C++
code easily. Note in my code, I also added an albedo
to the material, but you can remain to use just a white color.
A modified version of the two spheres scene, called four spheres is similar to Shirley’s. This looks like
For dielectric material, it’s also similar. The core implementation is:
impl Surface for Dielectric {
fn scatter(&self, ray_in: Ray, rec: &SurfaceHit) -> Option<SurfaceInteraction> {
let ri = if rec.is_front {
1.0 / self.ior
} else {
self.ior
};
let cos_theta = (-ray_in.direction.dot(rec.normal)).min(1.0);
let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
let refract = ri * sin_theta <= 1.0;
let dir = if !refract || Self::reflectance(cos_theta, ri) > rand::random() {
ray_in.direction.reflect(rec.normal)
} else {
ray_in.direction.refract(rec.normal, ri)
};
Some(SurfaceInteraction {
attenuation: Color::new(1.0, 1.0, 1.0),
scattered: Ray::new(rec.position, dir),
})
}
}
This and used auxiliary methods are simple, and almost just translation of the C++
code, so the whole code is not listed here. Change the left sphere to use the new Dielectric
material, we can have a rendered result like:
And I also tested a high resolution version, which looks pretty well, right?
Using \(180 \times 120\) as tile size, it costs me over 4 minutes, while the parallel version costs only less than 1 minute.