我们的相机之前一直都是固定死的,这次让我们来做些改变,让我们使用一个可调整的视野(fov)。 先看一下我们之前是如何设置camera的:[

](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051706.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051706.png)
特别需要注意的是,这是在z=-1的平面上。 当时是这么说的:已知Z=-1平面上四点的坐标,我们可以求出下面三个值。

  • 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);

然后,光线和平面的交点坐标可以表示为向量:lower_left_corner + u*horizontal + v*vertical。这个即为光线的方向向量。 我们要重新配置camera,引入张角 theta 和画面宽高比 aspect: [

](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051707.png)
设整个画面的高为
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一般用的是弧度,而我们在给函数设置参数时使用角度更为方便。所以,引入角度vfov=theta*180/M_PI(即,theta = vfov * M_PI/180)。ofov有个专业的名称:fieldof view 对于任意观测点,我们称: 放相机的位置为lookfrom,我们看的点(画面中心点)为lookat,相机的倾斜方向为view up (简称vup,一般设置为(0,1,0)) [

](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051710.png)](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051710.png) 有了这三个东东,我们现在就要找出相机在任意点的标准正交基(也就是相机要建立自己的坐标系,然后在自己的坐标系看之前坐标系的物体)。 w = unit_vector(lookfrom - lookat)(w相当于z) u = unit_vector(cross(vup, w))(u相当于x) v = cross(w, u)(v相当于y) [![](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051711.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051710.png)](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051710.png) 有了这三个东东,我们现在就要找出相机在任意点的标准正交基(也就是相机要建立自己的坐标系,然后在自己的坐标系看之前坐标系的物体)。 w = unit_vector(lookfrom - lookat)(w相当于z) u = unit_vector(cross(vup, w))(u相当于x) v = cross(w, u)(v相当于y) [![](http://www.wjgbaby.com/wp-content/uploads/2018/05/18051711.png)
w为什么相当于z?这个是由放相机的位置(lookfrom)和目标点(lookat)确定的。 u为什么相当于x?因为相机倾斜方向vup和y比较接近,所以vup和w叉乘之后就是x。 v为什么相当于y?z、x都确定了,还有的选么?另外,w和u的叉乘相当于z、x的叉乘,结果也是y。 camera.h:

#ifndef cameraH
#define cameraH

//M_PI 是一个宏定义,圆周率的定义,math文件中也有这家伙。
#define M_PI 3.14159265358979323846

#include “ray.h”

class camera
{
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;

public:
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect)
{
    vec3 u, v, w;
    //theta张角
    float theta = vfov \* M\_PI / 180;
    //一半的高度等于tan(theta/2)
    float half\_height = tan(theta / 2);
    //aspect画面宽高比例
    float half\_width = aspect \* half\_height;

    //lookform相机位置
    origin = lookfrom;
    //lookat相机看相的点
    //w得到一个相机正前方的向量
    //垂直于 w 与 view of up(简写为vup)的向量即为“平面上”横向的向量,
    //垂直于横向向量与 w 的向量即为竖向向量
    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);
}

};
#endif

cpp:

#include
#include
#include
#include “sphere.h”
#include “hitable_list.h”
#include “camera.h”
#include “material.h”

using namespace std;

//获得反射射线
vec3 RandomInUnitsphere()
{
vec3 p;
do{
p = 2.0f * vec3((rand() % 100 / float(100)), (rand() % 100 / float(100)), (rand() % 100 / float(100))) - vec3(1.0f, 1.0f, 1.0f);
} while (dot(p, p) >= 1.0f);

return p;

}

vec3 Color(const ray& r, hitable* world, int depth)
{
//这个“rec”会在sphere::hit ()中带上来被撞击球的材料属性(指向一个材质对象的指针mat_ptr)。
//根据这个指针可以获取材料对象的成员方法scatter()和成员变量albedo(反射衰减向量)
hit_record rec;
if (world->hit(r, 0.001f, FLT_MAX, rec))
{
ray scattered;
vec3 attenuation;
if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered))
{
//获取反射光线向量scattered和反射衰减向量attenuation
return attenuation * Color(scattered, world, depth + 1);
//反射光线的强度需要乘以反射衰减向量(对应坐标相乘作为新的向量)。
//然后反射光线就扮演之前“原始光线”的角色。如果再次撞击到小球,就再次反射,直到不再撞击到任何球为止
}
else
{
return vec3(0.0f, 0.0f, 0.0f);
}
}
else
{
//绘制背景
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5f * (unit_direction.y() + 1.0f);

    //线性混合,t=1时蓝色,t=0时白色,t介于中间时是混合颜色
    //blended\_value = (1-t)\*start\_value + t\*end\_value
    return (1.0f - t) \* vec3(1.0f, 1.0f, 1.0f) + t \* vec3(0.5f, 0.7f, 1.0f);

    //注意这里,原始光线和反射光线最后都会跑到这里来。
    //背景的颜色:原始光线的方向向量的映射
    //漫反射材料和镜面材料的颜色:最后一次反射光线的方向向量的映射 \*  所有反射衰减系数的乘积。
    //漫反射和镜面反射的区别在于,漫反射的每次反射方向是随机的
}

}

//And add some metal spheres
int main()
{
ofstream outfile;
outfile.open(“IMG2.ppm”);

int nx = 800;
int ny = 400;
//采样次数
int ns = 100;
outfile << "P3\\n" << nx << " " << ny << "\\n255\\n";

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 hitable\_list(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), 60, float(nx) / float(ny));

//随机数
default\_random\_engine reng;
uniform\_real\_distribution<float> uni\_dist(0.0f, 1.0f);

for (int j = ny - 1; j >= 0; j--)
{
    for (int i = 0; i < nx; i++)
    {
        vec3 col(0.0f, 0.0f, 0.0f);
        //每个区域采样ns次
        for (int s = 0; s < ns; s++)
        {
            float u = float(i + uni\_dist(reng)) / float(nx);
            float v = float(j + uni\_dist(reng)) / float(ny);
            ray r = cam.getray(u, v);
            //vec3 p = r.point\_at\_parameter(2.0);
            //将本区域((u,v)到(u+1,v+1))的颜色值累加
            col += Color(r, world, 0);
        }
        //获得区域的颜色均值
        col /= float(ns);
        //gamma矫正
        col = vec3(sqrt(col\[0\]), sqrt(col\[1\]), sqrt(col\[2\]));
        int ir = int(255.99 \* col\[0\]);
        int ig = int(255.99 \* col\[1\]);
        int ib = int(255.99 \* col\[2\]);
        outfile << ir << " " << ig << " " << ib << "\\n";
    }
}
outfile.close();
return 0;

}

设置fov为60度时,效果如下: 设置fov为90度时,效果如下: 参考链接:https://blog.csdn.net/libing\_zeng/article/details/54560334 参考书籍:《Ray Tracing in One Weekend》 RTIOW系列项目地址:GitHub RTIOW系列笔记: RTIOW-ch1:Output an image RTIOW-ch2:The vec3 class RTIOW-ch3:Rays, a simple camera, and background RTIOW-ch4:Adding a sphere RTIOW-ch5:Surface normals and multiple objects RTIOW-ch6:Antialiasing RTIOW-ch7:Diffuse Materials RTIOW-ch8:Metal RTIOW-ch9:Dielectrics RTIOW-ch10:Positionable camera RTIOW-ch11:Defocus Blur RTIOW-ch12:Where next