Difficulty |
2 |
Prerequisites |
|
Reading material |
def scene_at(now)
{
var camera = Cameras.perspective( [ "eye": pos(0,0,5),
"look_at": pos(0,0,0) ] )
var floor_material = Materials.uniform( [ "ambient": Colors.white() * 0.1,
"diffuse": Colors.white() * 0.8,
"reflectivity": 0.5 ] )
var left_wall_material = Materials.uniform( [ "ambient": Colors.red() * 0.1,
"diffuse": Colors.red() * 0.8 ] )
var right_wall_material = Materials.uniform( [ "ambient": Colors.green() * 0.1,
"diffuse": Colors.green() * 0.8 ] )
var back_wall_material = Materials.uniform( [ "ambient": Colors.blue() * 0.1,
"diffuse": Colors.blue() * 0.8 ] )
var ceiling_material = floor_material
var sphere_material = Materials.uniform( [ "ambient": Colors.blue() * 0.1,
"diffuse": Colors.blue() * 0.8,
"specular": Colors.white() * 0.8,
"specular_exponent": 10,
"transparency": 0.7,
"refractive_index": 2.5 ] )
var small_sphere_material = Materials.uniform( [ "ambient": Colors.white() * 0.1,
"diffuse": Colors.white() * 0.8,
"reflectivity": 0.8 ] )
var primitives = []
primitives.push_back( translate(vec(0,-2,0), decorate(floor_material, xz_plane())) )
primitives.push_back( translate(vec(0,2,0), decorate(ceiling_material, xz_plane())) )
primitives.push_back( translate(vec(-2,0,0), decorate(left_wall_material, yz_plane())) )
primitives.push_back( translate(vec(2,0,0), decorate(right_wall_material, yz_plane())) )
primitives.push_back( translate(vec(0,0,-2), decorate(back_wall_material, xy_plane())) )
var sphere_position = Animations.circular( [ "position": pos(0,0,1),
"around": pos(0,0,0),
"duration": seconds(5) ] )
primitives.push_back( decorate( sphere_material, translate(sphere_position[now] - pos(0,0,0), scale(0.5, 0.5, 0.5, sphere())) ) )
primitives.push_back( decorate( small_sphere_material, scale(0.25, 0.25, 0.25, sphere()) ) )
var root = union(primitives)
var lights = [ Lights.omnidirectional( pos(0,1.9,0), Colors.white() ) ]
create_scene(camera, root, lights)
}
var raytracer = Raytracers.v3()
var renderer = Renderers.standard( [ "width": 500,
"height": 500,
"sampler": Samplers.multijittered(2),
"ray_tracer": raytracer ] )
pipeline( scene_animation(scene_at, seconds(5)),
[ Pipeline.animation(30),
Pipeline.renderer(renderer),
Pipeline.studio() ] )
1. Background Information
Ray tracer v1 gave us ambient lighting. Ray tracer v2 improved upon this by introducing diffuse lighting. Ray tracer v3 will add specular highlights, which enables us to make objects look metallic.
2. Additional Material Properties
2.1. MaterialProperties
Open the file |
As you can see, the MaterialProperties
class currently only contains fields for ambient and diffuse lighting.
We will need to add fields so that materials can also exhibit specular highlights.
In the class
Add extra parameters to the constructor to be used as initial values for these fields. |
2.2. MaterialPropertiesBuilder
The MaterialProperties
class will grow large as we develop more ray tracers.
Having to specify values for each parameter will soon become cumbersome, especially since there will be reasonable default values for them.
For this reason, a MaterialPropertiesBuilder
class exists.
It simplifies the creation of a MaterialProperties
object:
MaterialProperties material_properties(ambient, diffuse, specular, specular_exponent);
// can be written as
auto material_properties =
create_material_properties_with().ambient(ambient)
.diffuse(diffuse)
.specular(specular, specular_exponent);
The builder provides the following advantages:
-
We can specify the arguments in any order we want.
-
We can leave out any of the arguments and they will automatically be initialized to default values.
-
It is much more readable as the field being initialized is being mentioned by name. With the regular constructor syntax, there is no hint whatsoever as to what the nth argument means.
Extend
You can use the existing code for |
2.3. Bindings
Open |
3. RayTracerV3
Ray tracer v3 will build upon v2.
In code, this is reflected by the fact that RayTracerV3
should be a subclass of RayTracerV2
.
3.1. process_light_ray
Remember process_light_ray
from v2:
it is called once for every light ray emitted by each light source in the scene.
In the case of v2, it merely called compute_diffuse
and returned that function’s result.
In v3, we can override this function so that instead of only returning the diffuse color, it adds the specular highlight to this result.
Override
|
3.2. compute_specular
Implement the specular highlight algorithm as a new protected method named
|
3.3. Finishing Touches
Create the factory function |
Expose this factory function to the scripting language by adding the necessary code in |
4. Evaluation
Reproduce the scene below. Make sure to pay attention to the size of the specular highlights. |