' '

Difficulty

2

Prerequisites

math/quaternions

ray-tracers/v1

Reading material

animations/basics

design/animations

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

def electron()
{
  decorate( material(Colors.red()), scale(0.1, 0.1, 0.1, sphere()) )
}


def scene_at(now)
{
  var camera_position = Animations.circular([ "position": pos(0,0,5),
                                              "around": pos(0,0,0),
                                              "duration": seconds(5) ])

  var camera = Cameras.perspective( [ "eye": camera_position[now],
                                      "look_at": pos(0,0,0) ] )

  var p1 = Animations.circular([ "position": pos(0,0,1),
                                 "around": pos(0,0,0),
                                 "duration": seconds(1) ])

  var p2 = Animations.circular([ "position": pos(cos(degrees(120)),sin(degrees(120)),0),
                                 "around": pos(0,0,0),
                                 "axis": vec(cos(degrees(30)), sin(degrees(30)),0),
                                 "duration": seconds(1) ])

  var p3 = Animations.circular([ "position": pos(cos(degrees(60)),sin(degrees(60)),0),
                                 "around": pos(0,0,0),
                                 "axis": vec(cos(degrees(150)), sin(degrees(150)),0),
                                 "duration": seconds(1) ])

  var nucleus = decorate(material(Colors.blue()), scale(0.25, 0.25, 0.25, sphere()))
  var e1 = translate(p1[now] - pos(0,0,0), electron())
  var e2 = translate(p2[now] - pos(0,0,0), electron())
  var e3 = translate(p3[now + seconds(0.25)] - pos(0,0,0), electron())

  var root = union([ nucleus, e1, e2, e3 ])

  var lights = [ Lights.omnidirectional( pos(0,5,5), Colors.white() ), 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(150),
            Pipeline.renderer(renderer),
            Pipeline.motion_blur(5, 5),
            Pipeline.studio() ] )

1. Implementation

We want to be able to rotate a point around an arbitrary axis. This includes axes that do not go through \((0, 0, 0)\). However, quaternions can only represent rotations around axes through \((0, 0, 0)\). You will have to find a trick to circumvent this limitation.

Create the files animation/circular-animation.cpp and animation/circular-animation.h and define a factory function named circular:

Animation<math::Point3D> animation::circular(
    const math::Point3D&               point,
    const math::Point3D&               center,
    const math::Vector3D&              rotation_axis,
    const math::Interval<math::Angle>& angle_interval,
    const Duration&                    duration
)
  • point: original position.

  • center: point around which animation revolves.

  • rotation_axis: normal on the plane in which the circular motion takes place.

  • angle_interval: start and end angle. Start angle should be 0°.

  • Duration: how long the animation takes.

  • Use animation/interval-animation.h as inspiration for how to go about implementing the animation. Don’t forget to add a binding for the scripting language.

2. Evaluation

Render the following scene:

def material(color)
{
  Materials.uniform( [ "ambient": color ] )
}

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

  var primitives = []

  for ( var i = 0; i != 11; ++i )
  {
    var t = i / 10.0
    var alpha = degrees(t * 360.0 * 2)

    var p =  Animations.circular([ "position": pos(-5 + cos(alpha), -10+2*i, sin(alpha)),
                                   "around": pos(-5,0,0),
                                   "axis": vec(0, 1, 0),
                                   "duration": seconds(1) ])[now]

    var s = decorate(material(Colors.red()), sphere())

    primitives.push_back(translate(p - pos(0,0,0), s))
  }

  for ( var i = 0; i != 10; ++i )
  {
    var t = i / 10.0
    var alpha = degrees(t * 360.0)

    var p =  Animations.circular([ "position": pos(5 + 4*cos(alpha), 4*sin(alpha), 0),
                                   "around": pos(5,0,0),
                                   "axis": vec(0, 0, 1),
                                   "duration": seconds(5) ])[now]

    var s = decorate(material(Colors.blue()), sphere())

    primitives.push_back(translate(p - pos(0,0,0), s))
  }


  var root = union( primitives )

  var lights = [ ]

  create_scene(camera, root, lights)
}


var raytracer   = Raytracers.v1()
var renderer    = Renderers.standard( [ "width": 500,
                                        "height": 500,
                                        "sampler": Samplers.single(),
                                        "ray_tracer": raytracer ] )

pipeline( scene_animation(scene_at, seconds(5)),
          [ Pipeline.animation(30),
            Pipeline.renderer(renderer),
            Pipeline.studio() ] )