0%

十六:时空光追

曝光时间

初篇的最后我们一直在和相机较劲,一个拥有景深(散焦模糊)的相机也并不是我们相机进化的终点,在本篇的开始,我们将让相机进化为完全形态。这将是本系列最后一次改动相机类,在本篇之后,相机类将不会再有任何改动——当然,你自己可以对其进行一些个性化改动或者添加你认为需要的功能,但是对于我们介绍的所有功能中,相机将进化为完全体。

真实的相机,有一个概念叫做曝光时间:它表示镜头打开的时间,在这段时间内射到相机的光线都会被相机捕捉。

但是我们当前的相机,曝光时间是趋于无穷小的,也就意味着,任何高速移动的物体,在相机镜头打开的这一瞬间里,移动的距离都为0

依照我们在散焦模糊那一章的处理思路,我们得自断一臂,通过某些方式让我们的相机产生缺陷去迎合真实的相机。

要怎么模拟曝光时间这一概念呢?按照逆光路模型,我们得让光线在一段时间内先后从相机射出,而不是一瞬间。按照曝光时间的长短,让某一个像素中射出的若干采样光线在此曝光时间内的随机时刻射出,并带回信息即可。

要想实现这一点,需要先改动光线类,让光线和时间挂钩。

请不要把这个时间和我们光线公式中 P( t ) = A + tb 的 t 弄混,这个 t 表示这一根光线在发射之后过了多少时间,并不涉及到光源发射的时间。

看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ray {
public:
ray() {}
// 更改构造函数,tm的缺省默认值为0。
ray(const point3& origin, const vec3& direction, double time = 0.0)
: orig(origin), dir(direction), tm(time)
{}

point3 origin() const { return orig; }
vec3 direction() const { return dir; }

//tm的getter函数。
double time() const { return tm; }

point3 at(double t) const {
return orig + t*dir;
}

public:
point3 orig;
vec3 dir;

// 光线发射时刻。
double tm;
};

光线现在有发射时间的概念了,接下来修改发射光线的相机类,让相机能发射有不同tm值的光线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class camera {
public:
camera(
point3 lookfrom,
point3 lookat,
vec3 vup,
double vfov,
double aspect_ratio,
double aperture,
double focus_dist,
//相机快门打开时间。
double _time0 = 0,
//相机快门关闭时间,开关差值既是曝光时间。
double _time1 = 0
) {
auto theta = degrees_to_radians(vfov);
auto h = tan(theta/2);
auto viewport_height = 2.0 * h;
auto viewport_width = aspect_ratio * viewport_height;

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

origin = lookfrom;
horizontal = focus_dist * viewport_width * u;
vertical = focus_dist * viewport_height * v;
lower_left_corner = origin - horizontal/2 - vertical/2 - focus_dist*w;

lens_radius = aperture / 2;
//开关时间初始化
time0 = _time0;
time1 = _time1;
}

ray get_ray(double s, double t) const {
vec3 rd = lens_radius * random_in_unit_disk();
vec3 offset = u * rd.x() + v * rd.y();

return ray(
origin + offset,
lower_left_corner + s*horizontal + t*vertical - origin - offset,
//光线的tm值给一个time0和time1中的随机值。
random_double(time0, time1)
);
}

private:
point3 origin;
point3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 u, v, w;
double lens_radius;
//快门开关时间。
double time0, time1;
};

现在我们拥有了一个能在曝光时间内随机发射光线的相机!你可能会觉得这根本不算什么,就是给光线类多加了一个成员变量罢了,但这对我们的渲染器来说是跨时代的一步。我们的光追器不再仅仅只是描绘光线在空间上的碰撞,而且开始描绘时间上的交错变化。这叫做时空光线追踪(SpaceTime Ray Tracing)。

参考文献

https://raytracing.github.io/books/RayTracingTheNextWeek.html

参考自《Ray Tracing: The Next Week》第1节和第2.1节。