' '

Difficulty

2

Prerequisites

ray-tracers/v4

Reading material

lights/directional

1. Directional vs Omnidirectional

The provided light (omnidirectional) is like a tiny lamp that sends light in all directions. Imagine that you put this lamp "infinitely" far away, like the sun. All light rays will appear to arrive in the same direction.

Below you can compare both types of light:

Omnidirectional

Directional

Notice that since, in the case of omnidirectional light, all light comes from a single point, shadows "fan out" from this point. Directional light, however, produces shadows that are perfectly parallel.

2. Implementation

Create a lights/directional-light.cpp and lights/directional-light.h. Its factory function is

LightSource directional(const math::Vector3D& direction,
                        const imaging::Color& color);

The implementation is very short:

class DirectionalLight(LightSourceImplementation):
  def __init__(self, direction, color):
    self.__direction = direction
    self.__color = color

  def lightrays_to(point):
    fake_origin = ???
    ray = Ray(fake_origin, point)
    lightray = LightRay(ray, self.__color)

    assert ray.direction.normalized() == self.__direction.normalized()
    assert lightray.ray.at(1) == point

    return [lightray]
Tip

A directional light is the same as an omnidirectional light that’s located very far away in a specific direction. fake_origin can be computed by moving away from point in the opposite direction given by __direction.

Important

Remember that the light ray you return must obey the following rule in order for shadows to work:

  • lightray.ray.at(0) must be the origin of the light source (i.e., far away).

  • lightray.ray.at(1) must be equal to point.

  • Update scripting/lights-module.cpp.

  • Add bindings for directional light in scripting/lights-module.cpp.

3. Evaluation

Render the scene below:

def scene_at(now)
{
  var t = Animations.animate(0, 1, seconds(5))[now]

  var camera = Cameras.perspective( [ "eye": pos(10*t,20-15*t,10-5*t),
                                      "look_at": pos(0,0,10*t) ] )

  var material = Materials.uniform( [ "ambient": Colors.black(),
                                      "diffuse": Colors.white() ] )

  var primitives = []

  for ( var x = -3*10; x <= 3*10; x += 3 )
  {
    for ( var y = 0; y <= 10; y += 2 )
    {
      primitives.push_back( translate(vec(x,y,0), sphere()) )
    }

    primitives.push_back( translate(vec(x,0,10), sphere()) )
  }

  var root = decorate(material, union([ translate(vec(0,-1,0),xz_plane()), union( primitives ) ]))



  var p = Animations.animate(-1,1,seconds(5))
  var lights = [ Lights.directional( vec(p[now],-1,1).normalized(), Colors.white() ) ]

  create_scene(camera, root, lights)
}

var raytracer   = Raytracers.v6()
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() ] )