本项目上接《用两天学习光线追踪》,继续学习光线追踪。
项目链接:https://github.com/maijiaquan/ray-tracing-with-imgui
目录:
《用两天学习光线追踪》1.项目介绍和ppm图片输出
《用两天学习光线追踪》2.射线、简单相机和背景输出
《用两天学习光线追踪》3.球体和表面法向量
《用两天学习光线追踪》4.封装成类
《用两天学习光线追踪》5.抗锯齿
《用两天学习光线追踪》6.漫反射材质
《用两天学习光线追踪》7.反射向量和金属材质
《用两天学习光线追踪》8.折射向量和电介质
《用两天学习光线追踪》9.可放置相机
《用两天学习光线追踪》10.散焦模糊
《用一周学习光线追踪》1.动态模糊
《用一周学习光线追踪》2.BVH树、AABB相交检测
《用一周学习光线追踪》3.纯色纹理和棋盘纹理
《用一周学习光线追踪》4.柏林噪声
《用一周学习光线追踪》5.球面纹理贴图
《用一周学习光线追踪》6.光照和轴对齐矩形
《用一周学习光线追踪》7.长方体和平移旋转
上章回顾
上一章《用两天学习光线追踪》中,我们制作了一个简单的光线追踪器,具体渲染流程如下:
1.对一个宽高为nx,ny的屏幕,遍历每个像素
2.每个像素再遍历ns次,以实现多重采样抗锯齿
3.在该位置发射一道射线,并进行递归颜色采样,输出到屏幕
本节内容
增加动态模糊的效果。代码改动:
1.ray增加一个参数t
2.camera生成随机数并赋值给ray的t
3.增加moving_sphere类
动态模糊
如果要实动态模糊的效果,可以将原来的球体,改成球心在某条线段上的球体,例如代码中将sphere修改为moving_sphere,并将原来的center
修改成center0
, center1
。每条射线都会有一个随机的系数t(范围0到1),使用t插值center0
, center1
,得到这条线段上的某个点作为moving_sphere的球心。
不同的射线会有不同的t,不同的t插值出来的球心位置就会不同。对某个像素随机采样ns次之后,这个像素就会产生ns条t值随机的射线,这ns条射线对应位置不同的球,从而渲染出一个模糊的像素。渲染这条线段附近区域的包含该球体的所有像素,就能看到球在这条线段上运动的模糊效果,从而模拟相机快门的长曝光时间。
ray增加参数:
class ray
{
public:
...
float _time;
...
ray(const vec3 &a, const vec3 &b, float ti = 0.0)
{
...
_time = ti;
}
...
float time() const { return _time; }
...
};
camera增加随机产生time赋值给ray:
class camera
{
public:
...
float time0, time1;
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect, float aperture, float focus_dist, float t0, float t1)
{
time0 = t0;
time1 = t1;
...
}
ray get_ray(float s, float t)
{
...
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;
...
vec3 center(float time) const
{
return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0);
}
bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const
{
vec3 oc = r.origin() - center(r.time());
...
if (discriminant > 0)
{
...
if (temp < t_max && temp > t_min)
{
...
rec.normal = (rec.p - center(r.time())) / radius;
...
}
...
if (temp < t_max && temp > t_min)
{
...
rec.normal = (rec.p - center(r.time())) / radius;
...
}
}
...
}
};