' '

Difficulty

2

Prerequisites

math/transformations

Reading material

design/transformations

def colored_material(color) {
  Materials.uniform( [ "ambient": color * 0.1,
                       "diffuse": color * 0.8,
                       "specular": Colors.white(),
                       "specular_exponent": 10,
                       "reflectivity": 0.2 ] )
}

def scene_at(now)
{
  var camera = Cameras.perspective( [ "eye": pos(0,0,10),
                                      "look_at": pos(0,0,0) ] )

  var cylinder = crop_along_y(cylinder_along_y(), interval(-1,1))

  var red_cylinder = decorate(colored_material(Colors.red()), cylinder)
  var green_cylinder = decorate(colored_material(Colors.green()), cylinder)
  var blue_cylinder = decorate(colored_material(Colors.blue()), cylinder)

  var angle = Animations.animate(degrees(0), degrees(360), seconds(5))[now]
  var factor = Animations.animate(1, 2, seconds(5))[now]
  var x = Animations.animate(-10, 10, seconds(5))[now]

  var root = union([
    translate(vec(x, 0, -5), red_cylinder),
    translate(vec(-2, 0, 0), rotate_around_x(angle, green_cylinder)),
    translate(vec(2, 0, 0), scale(factor, factor, factor, blue_cylinder))
  ])

  var lights = [ Lights.omnidirectional( pos(0,5,5), Colors.white() ) ]

  create_scene(camera, root, lights)
}

var raytracer   = Raytracers.v6()

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. Introduction

A ray tracer can support many shapes, such as spheres, cylinders, cones, planes, triangles, boxes and so on. When building a scene, we want to be able to place these shapes anywhere we want, resize them and rotate them on a whim.

While it would be possible to parameterize all these primitives so that each of them can have a chosen position, size and rotation, this would make their implementation more complex. Instead, we choose another approach: each primitive should only be implemented in its most simple form. For example, a sphere should be centered at \((0,0,0)\) and have radius 1, as these parameters maximally simplify the formulae involved.

So what if we want a bigger sphere? Instead of trying to make the sphere bigger, we make the camera smaller. The nice part about this approach is that we can apply the same trick on all primitives. Similarly, if we want to move the sphere up, we can simply move the camera down instead. Rotation can be handled the same way.

2. Translation

Translation consists of moving a shape to a different position. Luckily for you, this has already been implemented: you can find the code in primitives/transformer-primitive.cpp. The code can act as a guide for you to implement the two other transformations (rotation and scaling.)

3. Rotation

Open primitives/transformer-primitive.cpp. You can use the given implementation of raytracer::primitives::translate as a guide.

Add three functions with signatures

  • Primitive rotate_around_x(Angle angle, Primitive child)

  • Primitive rotate_around_y(Angle angle, Primitive child)

  • Primitive rotate_around_z(Angle angle, Primitive child)

You will need to rely on the classes defined in this extension.

Add declarations to the corresponding header file so that these functions are accessible to other compilation units.

Note

You might wonder why we don’t simply use a single rotate(x_angle, y_angle, z_angle, child). We could, but the problem is that the order of rotation matters: rotating around X, then around Y yields different results than rotating around Y, then around X. Therefore, offering three separate functions would lead to less confusion.

4. Scaling

To the same file, add a function with signature

Primitive scale(double sx, double sy, double sz, Primitive child)

You will need to rely on the classes defined in this extension.

Add declarations to the appropriate header file so that these functions are accessible to the other compilation units.

5. Bindings

Add the necessary bindings so to make rotation available in scripts.

6. Evaluation

TODO