Ray Tracing The Next Week:chapter 1:运动模糊
文章翻译
当你决定光线追踪时,你觉得程序运行时,视觉质量更有价值。在你的镜面模糊反射和离焦模糊中,每个像素你需要多个样本。一旦你走下了这条路,好消息是几乎所有影响都是强制性的,运动模糊当然是其中之一。在真实的相机中,遮光器打开并在一个时间间隔里保持打开状态,相机和物体可能在那段时间内移动了。相机在那段间隔里所看到的是一个平均值,这是我们想要的。在遮光器开启时,我们可以通过在一段随机时间内发送几束光线来得到随机估计。只要这些物体在那段时间在它们该在的地方,我们可以用恰好在一次时间内的射线来得到正确的平均答案,这就是随机光线追踪为什么很简单的根本原因。
最基础的想法是,在遮光器开启的那段随机时间里产生光线,并在某时刻与模型交互。经常做的方法是,让相机和物体移动,但是在某时刻只有一束光线确切存在。用这种方法的引擎只需要确认,对于光线来说,这些物体的位置在哪,交互的内容没有太大改变。
为了实现这个,我们将首先让光线存储时间,实现如下:
ray.h
#include "vec3.h"
#pragma once
class ray
{
public:
ray();
ray(const vec3 &a, const vec3 &b, float ti = 0);
vec3 origin() const;
vec3 direction() const;
vec3 piont_at_parameter(float t) const;
float time() const;
public:
vec3 A;
vec3 B;
float _time;
};
ray.cpp
#include "ray.h"
ray::ray()
{
}
ray::ray(const vec3 &a, const vec3 &b, float ti = 0)
{
this->A = a;
this->B = b;
this->_time = ti;
}
vec3 ray::origin() const
{
return A;
}
vec3 ray::direction() const
{
return B;
}
vec3 ray::piont_at_parameter(float t) const
{
return A + B * t;
}
float ray::time() const
{
return _time;
}
现在我们需要修改相机,让其在 time1 和 time2 这段时间内随机产生光线,相机应该跟踪 time1 和 time2,还是在光线创建时由相机用户决定?由疑惑的时候,如果能让函数调用变得简单,我通常让构造器变得更复杂一些,因此我将让相机保持跟踪,当然这是个人的偏好。相机不需要有太多的变动,因为它现在还不允许移动;它仅在某段时间间隔内发送光线。
camera.h
#include "ray.h"
#pragma once
class camera
{
public:
camera();
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect, float aperture, float focus_dist, float t0, float t1);
ray get_ray(float u, float v);
public:
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 u, v, w;
float time0, time1;
float lens_radius;
};
camera.cpp
#include "camera.h"
#include "ray.h"
#define M_PI 3.14159265358979323846
#include <math.h>
camera::camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect, float aperture, float focus_dist, float t0, float t1)
{
time0 = t0;
time1 = t1;
lens_radius = aperture / 2;
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 * focus_dist - half_height * v * focus_dist - w * focus_dist;
horizontal = 2 * half_width * u * focus_dist;
vertical = 2 * half_height * v * focus_dist;
}
ray camera::get_ray(float s, float t)
{
vec3 rd = lens_radius * random_in_unit_disk();
vec3 offset = u * rd.x() + v * rd.y();
float time = time0 + drand48() * (time1 - time0);
return ray(origin + offset, lower_left_corner + s * horizontal + t * vertical - origin - offset, time);
}
camera::camera()
{
}
我们也需要一个移动中的物体,我们将创建一个球类,可以让其中心点线性地从 center0 移动到 center1,分别在 time0 和 time1 时刻。在时间间隔之外,它会继续,所以这些时间不需要和相机光圈启闭相匹配。
moving_sphere.h
#pragma once
#include "vec3.h"
#include "material.h"
class moving_sphere:public hitable
{
public:
moving_sphere();
moving_sphere(vec3 cen0, vec3 cen1, float t0, float t1, float r, material *m);
virtual bool hit(const ray &r, float tmin, float tmax, hit_record &rec) const;
vec3 center(float time) const;
public:
vec3 center0, center1;
float time0, time1;
float radius;
material *mat_ptr;
};
moving_sphere::moving_sphere(vec3 cen0, vec3 cen1, float t0, float t1, float r, material *m)
{
this->center0 = cen0;
this->center1 = cen1;
this->time0 = t0;
this->time1 = t1;
this->radius = r;
this->mat_ptr = m;
}
moving_sphere::vec3 center(float time) const
{
return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0);
}