本文主要参照 Ray Tracing: The Next Week,其中只是主要精炼光追相关理论,具体实现可参照原文。
一、动态模糊
如果要实动态模糊的效果,可以将原来的球体,改成球心在某条线段上的球体,例如代码中将sphere修改为moving_sphere,并将原来的center修改成center0, center1。每条射线都会有一个随机的系数t(范围0到1),使用t插值center0, center1,得到这条线段上的某个点作为moving_sphere的球心。
不同的射线会有不同的t,不同的t插值出来的球心位置就会不同。对某个像素随机采样ns次之后,这个像素就会产生ns条t值随机的射线,这ns条射线对应位置不同的球,从而渲染出一个模糊的像素。渲染这条线段附近区域的包含该球体的所有像素,就能看到球在这条线段上运动的模糊效果,从而模拟相机快门的长曝光时间。
ray增加参数:
class ray
{
public:
vec3 A; //起点
vec3 B; //方向
float _time;
ray() {}
ray(const vec3 &a, const vec3 &b, float ti=0.0f)
{
A = a;
B = b;
_time = ti;
}
float time() const { return _time; }
vec3 origin() const { return A; }
vec3 direction() const { return B; }
vec3 point_at_parameter(float t) const { return A + t * B; } //终点的坐标
};
camera增加随机产生time赋值给ray:
class camera
{
public:
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 u, v, w;
float lens_radius;
float time0, time1;
//lookfrom为相机位置,lookat为观察位置,vup传(0,1,0),vfov为视野角度,aspect为屏幕宽高比
//aperture为光圈大小,focus_dist为相机到观察点的距离
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 * focus_dist * u - half_height * focus_dist * v - focus_dist * w;
horizontal = 2 * half_width * focus_dist * u;
vertical = 2 * half_height * focus_dist * v;
}
ray 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 + random_double() * (time1 - time0);
return ray(origin + offset, lower_left_corner + s * horizontal + t * vertical - origin - offset, time);
}
};
moving_sphere类:
class moving_sphere : public hittable
{
public:
vec3 center0, center1;
float time0, time1;
float radius;
material *mat_ptr; /* NEW */
vec3 center(float time) const
{
return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0);
}
moving_sphere() {}
moving_sphere(
vec3 cen0, vec3 cen1, double t0, double t1, double r, material *m)
: center0(cen0), center1(cen1), time0(t0), time1(t1), radius(r), mat_ptr(m)
{};
//如果命中了,命中记录保存到rec
virtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const
{
vec3 oc = r.origin() - center(r.time());
float a = dot(r.direction(), r.direction());
float b = dot(oc, r.direction());
float c = dot(oc, oc) - radius * radius;
float discriminant = b * b - a * c;
if (discriminant > 0)
{
float temp = (-b - sqrt(discriminant)) / a; //小实数根
if (temp < t_max && temp > t_min)
{
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center(r.time())) / radius;
rec.mat_ptr = mat_ptr; /* NEW */
return true;
}
temp = (-b + sqrt(discriminant)) / a; //大实数根
if (temp < t_max && temp > t_min)
{
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center(r.time())) / radius;
rec.mat_ptr = mat_ptr; /* NEW */
return true;
}
}
return false;
}
};
放置球体:
camera cam(lookfrom, lookat, vec3(0, 1, 0), 20, float(nx) / float(ny), aperture, dist_to_focus,0,0.5);
hittable *random_scene() {
int n = 500;
hittable **list = new hittable*[n + 1];
list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(vec3(0.5, 0.5, 0.5)));
int i = 1;
for (int a = -11; a < 11; a++) {
for (int b = -11; b < 11; b++) {
float choose_mat = random_double();
vec3 center(a + 0.9*random_double(), 0.2, b + 0.9*random_double());
if ((center - vec3(4, 0.2, 0)).length() > 0.9) {
if (choose_mat < 0.8) { // diffuse
if (b % 2 == 0) //动态模糊的球体
{
auto center2 = center + vec3(0, random_double(), 0);
list[i++] = new moving_sphere(center, center2, 0.0, 1.0, 0.2,
new lambertian(vec3(random_double()*random_double(),
random_double()*random_double(),
random_double()*random_double())
)
);
}
else
{
list[i++] = new sphere(center, 0.2,
new lambertian(vec3(random_double()*random_double(),
random_double()*random_double(),
random_double()*random_double())
)
);
}
}
else if (choose_mat < 0.95) { // metal
list[i++] = new sphere(center, 0.2,
new metal(vec3(0.5*(1 + random_double()),
0.5*(1 + random_double()),
0.5*(1 + random_double())),
0.5*random_double()));
}
else { // glass
list[i++] = new sphere(center, 0.2, new dielectric(1.5));
}
}
}
}
list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5));
list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1)));
list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0));
return new hittable_list(list, i);
}