Friday, April 3, 2015

Cameras in a distribution ray tracer

You need two things in a distribution ray tracer camera.  First is just to be able to have the light be gathered from a lens (disk) rather than a point.  Second in to move and rotate the lens.

A very generic way to generate viewing rays for pixels (i,j) for a square image with W = H in version 0.1 of the ray tracer looking up the -z axis is:

       vec3 ray_direction(-1.0 + 2.0*i/(W-1.0), -1.0 + 2.0*j/(H-1.0), -1.0);

The ray origin is just (0,0,0).   That can produce an image like this (see last blog posting). 
Distribution ray tracer with 100 non-stratified samples and a simple pinhole camera

Now we just need to make the ray origin a random point on the radius R XY disk centered at the origin:

   vec3 random_xy_disk() {
       vec3 p;
       do {
           p = vec3(2*drand48()-1,2*drand48()-1,0);
       } while (dot(p,p) > 1);
       return p;

   vec3 origin = R*random_xy_disk()
   ray_direction *= focus_distance; // distance to where things are in focus
   ray_direction -= origin;

Yes, there are more efficient and compact ways to do that.  We're into getting the program done ASAP here :).   Now all the rays for a given pixel will converge at z distance focus.

Now we need to move the camera lens to position "eye" (yeah bad legacy naming).   We want to focus on a point at position "lookat" (there are many other viewing systems like specify the gaze direction, and it's a matter of taste-- just use one!).   We need a "view up vector" which would be any vector from the center of the skull through the center parted hair of a person.  This will allow us to make an orthonormal basis uvw:

    vec3 w = eye - lookat;  // z axis goes behind us
    vec3 u = cross(w, vup);
    vec3 vv = cross(w, u);
    focus = w.length();

Add a horizontal field-of-view so we can go for non-square images and we get:
        float     float multiplier = tan(hfov/2);
        ray_origin = 0.2*random_xy_disk();
        vec3 ray_direction(-1.0 + 2.0*float(i)/(W-1.0), -1.0*H/W + 2.0*float(j)/(W-1.0), -1.0);
        ray_direction *= vec3(multiplier, multiplier, 1.0);
        ray_direction = focus*ray_direction - ray_origin;
        ray_origin = ray_origin.x()*u + ray_origin.y()*v + ray_origin.z()*w;
        ray_direction = ray_direction.x()*u + ray_direction.y()*v + ray_direction.z()*w;

And that gives:
Depth of field, viewing,  100 unstratified samples.


No comments: