2. Motion Blur
- 在前面,反走样是通过像素内取多个路径实现的,此外多条路径的选择也跟后面的漫反射、模糊反射、散焦模糊等一系列随机过程有关,如果继续暴力解法,也可以实现运动模糊。
- 在真实的相机中,照片的形成是对一段时间内光线的记录,与快门有关,因此,为了模拟摄影,加入运动模糊。
- 首先运动模糊采取随机生成快门开启时间内某个时刻的光线,在此之后该光路上的其他反射或者折射光线都是同一时刻的光线,对应的是同一个时刻的物体位置。因此,修改ray类,为其加入时间成员属性,时间属性默认为0,即统一所有光线为初始时刻,不进行运动模糊。
class ray {
public:
ray() {}
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; }
double time() const { return tm; }
point3 at(double t) const {
return orig + t*dir;
}
public:
point3 orig;
vec3 dir;
double tm;
};
- 然后,在camera类中加入time0与time1两个属性,作为快门开启时间起始点。这两个属性用于在getray时为返回的ray类在该区间内随机生成光线发射时间。
class camera {
public:
camera(
point3 lookfrom,
point3 lookat,
vec3 vup,
double vfov, // vertical field-of-view in degrees
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,
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; // shutter open/close times
};
- 到目前为止,就得到了光线时间随机生成的功能。紧接着,需们需要实现运动的物体,此处为运动球体moving_sphere类。moving_sphere与sphere类似,需要定义球心坐标、半径和材质,后两者不需要修改,但是球心坐标需要改变。原本的sphere类的球心坐标是point3数据成员,可以被hit函数直接使用,而在moving_sphere类中球心坐标通过定义成员函数moving_sphere::center实现,为了实现某个时刻球心坐标的计算,定义了四个成员变量
point3 center0, center1;double time0, time1;
,利用线性方式求解球心位置(此处为线性匀速运动)。进一步的,在hit函数的重写中,只需要将center换为center(r.time()),与之前的对应起来了,即利用光线的时间属性来得到物体的位置,基于此计算相交结果。
#ifndef MOVING_SPHERE_H
#define MOVING_SPHERE_H
#include "rtweekend.h"
#include "hittable.h"
class moving_sphere : public hittable {
public:
moving_sphere() {}
moving_sphere(
point3 cen0, point3 cen1, double _time0, double _time1, double r, shared_ptr<material> m)
: center0(cen0), center1(cen1), time0(_time0), time1(_time1), radius(r), mat_ptr(m)
{};
virtual bool hit(
const ray& r, double t_min, double t_max, hit_record& rec) const override;
point3 center(double time) const;
public:
point3 center0, center1;
double time0, time1;
double radius;
shared_ptr<material> mat_ptr;
};
point3 moving_sphere::center(double time) const {
return center0 + ((time - time0) / (time1 - time0))*(center1 - center0);
}
bool moving_sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
vec3 oc = r.origin() - center(r.time());
auto a = r.direction().length_squared();
auto half_b = dot(oc, r.direction());
auto c = oc.length_squared() - radius*radius;
auto discriminant = half_b*half_b - a*c;
if (discriminant < 0) return false;
auto sqrtd = sqrt(discriminant);
// Find the nearest root that lies in the acceptable range.
auto root = (-half_b - sqrtd) / a;
if (root < t_min || t_max < root) {
root = (-half_b + sqrtd) / a;
if (root < t_min || t_max < root)
return false;
}
rec.t = root;
rec.p = r.at(rec.t);
auto outward_normal = (rec.p - center(r.time())) / radius;
rec.set_face_normal(r, outward_normal);
rec.mat_ptr = mat_ptr;
return true;
}
#endif
- 最后,修改材质中的反射光线ray。
class lambertian : public material {
...
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
auto scatter_direction = rec.normal + random_unit_vector();
// Catch degenerate scatter direction
if (scatter_direction.near_zero())
scatter_direction = rec.normal;
scattered = ray(rec.p, scatter_direction, r_in.time());
attenuation = albedo;
return true;
}
...
};
class metal : public material {
...
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere(), r_in.time());
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
}
...
};
class dielectric : public material {
...
virtual bool scatter(
...
scattered = ray(rec.p, direction, r_in.time());
return true;
}
...
};
- 将diffuse小球给予y向运动:
...
#include "moving_sphere.h"
...
hittable_list random_scene() {
hittable_list world;
auto ground_material = make_shared<lambertian>(color(0.5, 0.5, 0.5));
world.add(make_shared<sphere>(point3(0,-1000,0), 1000, ground_material));
for (int a = -11; a < 11; a++) {
for (int b = -11; b < 11; b++) {
auto choose_mat = random_double();
point3 center(a + 0.9*random_double(), 0.2, b + 0.9*random_double());
if ((center - vec3(4, 0.2, 0)).length() > 0.9) {
shared_ptr<material> sphere_material;
if (choose_mat < 0.8) {
// diffuse
auto albedo = color::random() * color::random();
sphere_material = make_shared<lambertian>(albedo);
auto center2 = center + vec3(0, random_double(0,.5), 0);
world.add(make_shared<moving_sphere>(
center, center2, 0.0, 1.0, 0.2, sphere_material));
} else if (choose_mat < 0.95) {
// metal
auto albedo = color::random(0.5, 1);
auto fuzz = random_double(0, 0.5);
sphere_material = make_shared<metal>(albedo, fuzz);
world.add(make_shared<sphere>(center, 0.2, sphere_material));
} else {
// glass
sphere_material = make_shared<dielectric>(1.5);
world.add(make_shared<sphere>(center, 0.2, sphere_material));
}
}
}
}
auto material1 = make_shared<dielectric>(1.5);
world.add(make_shared<sphere>(point3(0, 1, 0), 1.0, material1));
auto material2 = make_shared<lambertian>(color(0.4, 0.2, 0.1));
world.add(make_shared<sphere>(point3(-4, 1, 0), 1.0, material2));
auto material3 = make_shared<metal>(color(0.7, 0.6, 0.5), 0.0);
world.add(make_shared<sphere>(point3(4, 1, 0), 1.0, material3));
return world;
}
int main() {
// Image
auto aspect_ratio = 16.0 / 9.0;
int image_width = 400;
int samples_per_pixel = 100;
const int max_depth = 50;
...
// Camera
point3 lookfrom(13,2,3);
point3 lookat(0,0,0);
vec3 vup(0,1,0);
auto dist_to_focus = 10.0;
auto aperture = 0.1;
int image_height = static_cast<int>(image_width / aspect_ratio);
camera cam(lookfrom, lookat, vup, 20, aspect_ratio, aperture, dist_to_focus, 0.0, 1.0);
效果如下:
标签:Week,const,auto,Tracing,Next,sphere,point3,double,ray From: https://www.cnblogs.com/etherovo/p/17343266.html