1. 程式人生 > >【Ray Tracing in One Weekend】(ch10)Positionable camera

【Ray Tracing in One Weekend】(ch10)Positionable camera

Chapter 10: Positionable camera

我們之前的 Camera 是被固定的,放在座標原點的,寫死的 Camera,現在我們要做一個可以隨意改變位置的 Camera。

先來回憶一下之前是如何表示 Camera 的:

這裡寫圖片描述

從一點(origin),看向一個平面(z=-1),同時用u、v乘以兩個向量(horizontal、vertical),並以左下角(lower_left_corner)為原點來定位平面上任一點,Ray 就由這幾個引數來確定:

Ray getRay(float u, float v) 
{
    return Ray(origin, lower_left_corner + u*horizontal + v*vertical - origin);
}

當時定義:

vec3 lower_left_corner(-2.0, -1.0, -1.0);

vec3 horizontal(4.0, 0.0, 0.0);

vec3 vertical(0.0, 2.0, 0.0);

現在,我們要為 Camera 定義新的引數來表示上面的值,首先引入張角 theta 和畫面寬高比 aspect:

這裡寫圖片描述

設整個畫面的高為 height,則半高為 half_height,寬為 width,半寬為 half_width。

則有:

half_height = tan(theta/2);

half_width = aspect * half_height;


vec3 lower_left_corner(-half_width, -half_height,-1.0
); vec3 horizontal(2*half_width, 0.0, 0.0); vec3 vertical(0.0, 2*half_height, 0.0);

現在我們僅僅是用新的引數 theta 和 aspect 表示了原來的 Camera,現在想把 Camera 架設到任意位置上,還需要引入 lookfrom(相機位置),lookat(相機看向的點),以及view of up(即表示相機正上的向量)。

可以先確定一個最容易理解的:

origin = lookfrom;

接著看看我們還需要什麼來確定畫面所在的“平面”,我們還需要一個指向相機正前方的向量,以確定平面的位置:

w = unit_vector(lookfrom - lookat);

垂直於 w 與 view of up(簡寫為vup)的向量即為“平面上”橫向的向量,垂直於橫向向量與 w 的向量即為豎向向量。

u = unit_vector(cross(vup, w));
v = cross(w, u);

綜上,可由此五個引數,確定如下四個值:

origin = lookfrom;
lower_left_corner = origin - half_width*u - half_height*v - w;
horizontal = 2 * half_width*u;
vertical = 2 * half_height*v;

如此一來,新的 Camera 類就寫好啦~ 程式碼如下:

#pragma once
#define _USE_MATH_DEFINES
#include "Ray.h"
#include <math.h>

class Camera 
{
public:
    //vfov: top to bottom in degrees
    Camera(Vec3 lookfrom, Vec3 lookat, Vec3 vup, float vfov, float aspect)
    {
        Vec3 u, v, w;
        float theta = vfov*M_PI / 180;
        float half_height = tan(theta / 2);
        float half_width = aspect * half_height;

        origin = lookfrom;
        w = unit_vector(lookfrom - lookat);
        u = unit_vector(cross(vup, w));
        v = cross(w, u);

        lower_left_corner = origin - half_width*u - half_height*v - w;
        horizontal = 2 * half_width*u;
        vertical = 2 * half_height*v;
    }

    Ray getRay(float u, float v) 
    {
        return Ray(origin, lower_left_corner + u*horizontal + v*vertical - origin);
    }

    Vec3 lower_left_corner;
    Vec3 origin;
    Vec3 horizontal;
    Vec3 vertical;
};

同時修改main方法:

    Hitable *list[5];
    list[0] = new Sphere(Vec3(0.0f, 0.0f, -1.0f), 0.5f, new Lambertian(Vec3(0.8f, 0.3f, 0.3f)));
    list[1] = new Sphere(Vec3(0.0f, -100.5f, -1.0f), 100.0f, new Lambertian(Vec3(0.8f, 0.8f, 0.0f)));
    list[2] = new Sphere(Vec3(1.0f, 0.0f, -1.0f), 0.5f, new Metal(Vec3(0.8f, 0.6f, 0.2f), 0.3f));
    list[3] = new Sphere(Vec3(-1.0f, 0.0f, -1.0f), 0.5f, new Dielectric(1.5f));
    list[4] = new Sphere(Vec3(-1.0f, 0.0f, -1.0f), 0.5f, new Dielectric(1.5f));
    Hitable *world = new HitableList(list, 5);

    Camera cam(Vec3(-2.0f,2.0f,1.0f), Vec3(0.0f,0.0f,-1.0f), Vec3(0.0f,1.0f,0.0f), 90, float(nx)/float(ny));

fov為90時,所得圖片如下:

這裡寫圖片描述

fov為40時,所得圖片如下:

這裡寫圖片描述