# T5 Position and Orient the Camera (Milestone 3)

### T5.1 Use Field-of-View angle (camera.h)

Now we are going to use the fov angle to control the camera zooming.

In camera.h, add a private memboer vfov.

In initialise(), calculate the viewport height based on vfov

<pre class="language-cpp"><code class="lang-cpp">class camera {
public:
    double aspect_ratio = 1.0;  // Ratio of image width over height
    int    image_width  = 100;  // Rendered image width in pixel count
    int    samples_per_pixel = 10; 
    int    max_depth = 10;   // Maximum number of ray bounces into scene

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    double vfov = 90;  // Vertical view angle (field of view)
    // &#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;
    
private:
    // ....
    
<strong>    void initialize() {
</strong>        image_height = int(image_width / aspect_ratio);
        image_height = (image_height &#x3C; 1) ? 1 : image_height;

        pixel_samples_scale = 1.0 / samples_per_pixel;

        center = point3(0, 0, 0);

        // Determine viewport dimensions.
        auto focal_length = 1.0;
        
        //auto viewport_height = 2.0;
        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        auto theta = degrees_to_radians(vfov);
        auto h = std::tan(theta/2);
        auto viewport_height = 2 * h * focal_length;
        // &#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;

        auto viewport_width = viewport_height * (double(image_width)/image_height);
        //...
   }
   //...
};
</code></pre>

#### Test in main() (main.cpp)

in main(), comment out those glass and metal sphere materials and objects, use the following simple scene in main() to test the viewing angle.

```cpp
int main() {
    hittable_list world;

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    auto R = std::cos(pi/4);

    auto material_left  = make_shared<lambertian>(color(0,0,1));
    auto material_right = make_shared<lambertian>(color(1,0,0));

    world.add(make_shared<sphere>(point3(-R, 0, -1), R, material_left));
    world.add(make_shared<sphere>(point3( R, 0, -1), R, material_right));
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    camera cam;
    cam.aspect_ratio = 16.0 / 9.0;
    cam.image_width = 400;
    cam.samples_per_pixel = 100;
    cam.max_depth         = 50;

    cam.vfov = 90; // <<<<<<<<<<<<<<<

    cam.render(world);

}
```

Build and Run, You are expected to see the following output:

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2FOwhEXGbGox2tMFf2Qkol%2Fimage.png?alt=media&#x26;token=8adb635c-ac9f-4003-a748-e72d6034ebbb" alt="" width="375"><figcaption></figcaption></figure>

The following is the output when setting fov to 100

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2FXgxsbOIvrLWe8n4Yh7bZ%2F3826131a-5525-4aaa-ba66-8ab5325634dc.png?alt=media&#x26;token=d9403eb1-7187-41ea-8efd-e39af3b6230e" alt="" width="375"><figcaption></figcaption></figure>

### T5.2 Position and Orient the Camera (camera.h)

#### Add lookfrom, lookat, vup, and u, v, w

Now we are going to set the camera frame using the camera position (lookfrom), the looking center (lookat), and the up vector (vup), in the same way as we are doing with glm::lookAt().

The difference is that we are going to calculate the camera frame vectors u, v, w, using vector cross product by ourselves.

First, add lookfrom, lookat, and vup in class camera's public members, after fov.

In the private section, add u, v, w as the camera's major axes.

```cpp
class camera {
public:
    double aspect_ratio = 1.0;  // Ratio of image width over height
    int    image_width  = 100;  // Rendered image width in pixel count
    int    samples_per_pixel = 10; 
    int    max_depth = 10;   // Maximum number of ray bounces into scene

    double vfov = 90;  // Vertical view angle (field of view)

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    point3 lookfrom = point3(0,0,0);   // Point camera is looking from
    point3 lookat   = point3(0,0,-1);  // Point camera is looking at
    vec3   vup      = vec3(0,1,0);     // Camera-relative "up" direction
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// ...
private:
    int    image_height;   // Rendered image height
    point3 center;         // Camera center
    point3 pixel00_loc;    // Location of pixel 0, 0
    vec3   pixel_delta_u;  // Offset to pixel to the right
    vec3   pixel_delta_v;  // Offset to pixel below
    double pixel_samples_scale;

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    vec3   u, v, w;              // Camera frame basis vectors
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<

// ...

};
```

#### Revise initialise() to calculate u, v, w of the camera frame from lookfrom, lookat, vup

```cpp
void initialize() {
        image_height = int(image_width / aspect_ratio);
        image_height = (image_height < 1) ? 1 : image_height;

        pixel_samples_scale = 1.0 / samples_per_pixel;

        // center = point3(0, 0, 0);
        center = lookfrom; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        // Determine viewport dimensions.
        // auto focal_length = 1.0;
        auto focal_length = (lookfrom - lookat).length();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        auto theta = degrees_to_radians(vfov);
        auto h = std::tan(theta/2);
        auto viewport_height = 2 * h * focal_length;
        auto viewport_width = viewport_height * (double(image_width)/image_height);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // Calculate the u,v,w unit basis vectors for the camera coordinate frame.
        w = unit_vector(lookfrom - lookat);
        u = unit_vector(cross(vup, w));
        v = cross(w, u);
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        // Calculate the vectors across the horizontal and down the vertical viewport edges.
        //auto viewport_u = vec3(viewport_width, 0, 0);
        //auto viewport_v = vec3(0, -viewport_height, 0);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        vec3 viewport_u = viewport_width * u;    // Vector across viewport horizontal edge
        vec3 viewport_v = viewport_height * -v;  // Vector down viewport vertical edge
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
        // Calculate the horizontal and vertical delta vectors from pixel to pixel.
        pixel_delta_u = viewport_u / image_width;
        pixel_delta_v = viewport_v / image_height;

        // Calculate the location of the upper left pixel.
        // auto viewport_upper_left = center - vec3(0, 0, focal_length) - viewport_u/2 - viewport_v/2;
        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        auto viewport_upper_left = center - (focal_length * w) - viewport_u/2 - viewport_v/2;
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
    }
```

#### Revise main() to test camera settings

Restore to the previous glass and metal sphere scene and set camera parameters

```
./ray01.exe > image16.ppm
```

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2Fir3jBfHUX4CzlWcWNe4g%2Ffbdc8b0e-07ac-4a44-b042-651a44d2c30b.png?alt=media&#x26;token=6f38414c-b4f8-4b23-8405-ed43e20b8506" alt="" width="375"><figcaption></figcaption></figure>

It looks a bit noisy

If we change the vfov of the camera to 20 in main(), you are going to see the following:

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2F3lOytA6nDU5cYJzd10he%2Fd0bd50b7-5ea8-4779-951b-9648dcbc731c.png?alt=media&#x26;token=c10b3ca2-3658-45b1-9335-b7d85cb69eff" alt="" width="375"><figcaption></figcaption></figure>

### T5.3 Defocus

#### choosing random points from the defocus disk

add random\_in\_unit\_disk() at the end of vec3.h&#x20;

```cpp
inline vec3 random_in_unit_disk() {
    while (true) {
        auto p = vec3(random_double(-1,1), random_double(-1,1), 0);
        if (p.length_squared() < 1)
            return p;
    }
}
```

#### add defocus members in class camera

```cpp
class camera {
private:
   // ...
    
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    vec3   defocus_disk_u;       // Defocus disk horizontal radius
    vec3   defocus_disk_v;       // Defocus disk vertical radius
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<
    
public:
    // ...
    
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    double defocus_angle = 0;  // Variation angle of rays through each pixel
    double focus_dist = 10;    // Distance from camera lookfrom point to plane of perfect focus
   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

};
```

#### Add a public method defocus\_disk\_sample() in class camera

```cpp
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    point3 defocus_disk_sample() const {
        // Returns a random point in the camera defocus disk.
        auto p = random_in_unit_disk();
        return center + (p[0] * defocus_disk_u) + (p[1] * defocus_disk_v);
    }
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
```

#### revise camera's initialise()

```cpp
    void initialize() {
        image_height = int(image_width / aspect_ratio);
        image_height = (image_height < 1) ? 1 : image_height;

        pixel_samples_scale = 1.0 / samples_per_pixel;

        center = lookfrom; 

        // Determine viewport dimensions.
        // auto focal_length = (lookfrom - lookat).length();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        auto theta = degrees_to_radians(vfov);
        auto h = std::tan(theta/2);
        // auto viewport_height = 2 * h * focal_length;  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        auto viewport_height = 2 * h * focus_dist;   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        auto viewport_width = viewport_height * (double(image_width)/image_height);

        // Calculate the u,v,w unit basis vectors for the camera coordinate frame.
        w = unit_vector(lookfrom - lookat);
        u = unit_vector(cross(vup, w));
        v = cross(w, u);

        vec3 viewport_u = viewport_width * u;    // Vector across viewport horizontal edge
        vec3 viewport_v = viewport_height * -v;  // Vector down viewport vertical edge
 
        // Calculate the horizontal and vertical delta vectors from pixel to pixel.
        pixel_delta_u = viewport_u / image_width;
        pixel_delta_v = viewport_v / image_height;

        // Calculate the location of the upper left pixel.
        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // auto viewport_upper_left = center - (focal_length * w) - viewport_u/2 - viewport_v/2;
        auto viewport_upper_left = center - (focus_dist * w) - viewport_u/2 - viewport_v/2;
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // Calculate the camera defocus disk basis vectors.
        auto defocus_radius = focus_dist * std::tan(degrees_to_radians(defocus_angle / 2));
        defocus_disk_u = u * defocus_radius;
        defocus_disk_v = v * defocus_radius;
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
```

#### revise camera's get\_ray() to change the ray origin a bit

```cpp
ray get_ray(int i, int j) const {
        // Construct a camera ray originating from the origin and directed at randomly sampled
        // point around the pixel location i, j.

        auto offset = sample_square();
        auto pixel_sample = pixel00_loc
                          + ((i + offset.x()) * pixel_delta_u)
                          + ((j + offset.y()) * pixel_delta_v);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // auto ray_origin = center;
        auto ray_origin = (defocus_angle <= 0) ? center : defocus_disk_sample();
        auto ray_direction = pixel_sample - ray_origin;
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        return ray(ray_origin, ray_direction);
    }
```

#### Set camera defocus parameters in main()

```cpp
void main() 
{
    // ...
    camera cam;
    cam.aspect_ratio = 16.0 / 9.0;
    cam.image_width = 400;
    cam.samples_per_pixel = 100;
    cam.max_depth         = 50;

    cam.vfov = 20; 
    cam.lookfrom = point3(-2,2,1);
    cam.lookat   = point3(0,0,-1);
    cam.vup      = vec3(0,1,0);
    
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    cam.defocus_angle = 10.0;
    cam.focus_dist    = 3.4;
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    cam.render(world);
}
```

#### Build and Run

```
./ray01.exe > image18.ppm
```

If successful, you are going to see outputs similar to the following:

It looks darker because I am using Gamma correction.

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2FrlIUXuhvOQAaBBRLXd61%2Fe417914f-e560-4b1d-93ec-20e6bbb2c7a7.png?alt=media&#x26;token=bbe89948-7af3-4a5d-9a59-65ab0c233e3d" alt="" width="375"><figcaption></figcaption></figure>
