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时,所得图片如下: