' '

Difficulty

2

Reading material

samplers/basics

samplers/stratified

samplers/testing

preview
var camera = Cameras.perspective( [ "eye": pos(5, 0.5, 5),
                                    "look_at": pos(0, 0.5, 0),
                                    "up": vec(0, 1, 0),
                                    "distance": 1,
                                    "aspect_ratio": 1 ] )

var white_material = Materials.uniform( [ "ambient": Colors.white(),
                                          "diffuse": Colors.black(),
                                          "specular": Colors.black(),
                                          "specular_exponent": 0 ] )

var black_material = Materials.uniform( [ "ambient": Colors.black(),
                                          "diffuse": Colors.black(),
                                          "specular": Colors.black(),
                                          "specular_exponent": 0 ] )

var checkered_material = Materials.from_pattern(Patterns.checkered(1, 1), white_material, black_material)

var root        = decorate(checkered_material, xz_plane())

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

var scene = create_scene(camera, root, lights)


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

pipeline( scene,
          [ Pipeline.renderer(renderer),
            Pipeline.studio() ] )

1. Implementation

Implement the stratified sampler. Make it public through a factory function

Sampler raytracer::samplers::stratified_fixed(unsigned rows, unsigned columns);
Tip

You can use the math::Rasterizer class to subdivide the rectangle into subrectangles.

Expose the sampler to the scripting language by updating scripting/samplers-module.cpp.

2. Evaluation

Render the scene below:

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

  var spheres   = []

  for_each([-10..10], bind(fun (i, spheres) {
    for_each([-10..10], bind(fun (j, i, spheres) {
      spheres.push_back(translate(vec(5*i, 0, 5*j), sphere()))
    }, _, i, spheres))
  }, _, spheres))

  var root      = union(spheres)

  var lights    = [ ]

  return create_scene(camera, root, lights)
}

var renderer = Renderers.standard( [ "width": 100,
                                     "height": 100,
                                     "sampler": Samplers.stratified(1,1),
                                     "ray_tracer": Raytracers.v0() ] )

pipeline( scene_animation(scene_at, seconds(1)),
          [ Pipeline.animation(30),
            Pipeline.renderer(renderer),
            Pipeline.wif(),
            Pipeline.base64(),
            Pipeline.stdout() ] )

Render the scene below:

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

  var spheres   = []

  for_each([-10..10], bind(fun (i, spheres) {
    for_each([-10..10], bind(fun (j, i, spheres) {
      spheres.push_back(translate(vec(5*i, 0, 5*j), sphere()))
    }, _, i, spheres))
  }, _, spheres))

  var root      = union(spheres)

  var lights    = [ ]

  return create_scene(camera, root, lights)
}

var renderer = Renderers.standard( [ "width": 100,
                                     "height": 100,
                                     "sampler": Samplers.stratified(3, 3),
                                     "ray_tracer": Raytracers.v0() ] )

pipeline( scene_animation(scene_at, seconds(1)),
          [ Pipeline.animation(30),
            Pipeline.renderer(renderer),
            Pipeline.wif(),
            Pipeline.base64(),
            Pipeline.stdout() ] )

Write at least 10 tests that check cases easy to compute manually, such as

  • Given a rectangle \([0,1]\times[0,1]\) and \(1 \times 1\) samples, the returned sample should be \(\{ (0.5, 0.5) \}\).

  • Given a rectangle \([5,7]\times[3,7]\) and \(1 \times 1\) samples, the returned sample should be \(\{ (6, 5) \}\).

  • Given a rectangle \([0,2]\times[0,2]\) and \(1 \times 1\) samples, the returned sample should be \(\{ (1, 1) \}\).

  • Given a rectangle \([0,4]\times[0,2]\) and \(2 \times 1\) samples, the returned samples should be \(\{ (1, 1), (3, 1) \}\).

  • Given a rectangle \([0,2]\times[0,4]\) and \(1 \times 2\) samples, the returned samples should be \(\{ (1, 1), (1, 3) \}\).

  • Given a rectangle \([0,4]\times[0,4]\) and \(4 \times 4\) samples, the returned samples should be \(\{ (i + 0.5, j + 0.5) \;|\; i = 0\dots3, j=0\dots3 \}\).

Important

The tests should not expect the samples to be returned in a certain order. During the defense, this will be checked. Failure to take this into account will disqualify this extension.

Tip

If you are uncertain of how to interpret the rectangle notation above (e.g., \([0,4]\times[0,4]\)), read this page.

Tip

See the reading material if you have no clue about how to write sampler tests.