简单的光线追踪教程(二)
4. 光线,简单的照相机和背景
4.1. 射线类
所有的光线追踪器都会有射线类,以及沿着射线看到什么颜色的计算。让我们把射线看作是一个函数
P
(
t
)
=
A
+
t
b
P(t) = A + tb
P(t)=A+tb
这里P是三维线上的三维位置。A是射线的起源bb是光线的方向。射线参数t是一个实数(在代码中用double修饰),通过不同的变量变换,我们可以得到射线或直线。
我们可以写一个类来表示射线类
#ifndef RAY_H
#define RAY_H
#include "vec3.h"
class ray {
public:
ray() {}
ray(const point3& origin, const vec3& direction)
: orig(origin), dir(direction)
{}
point3 origin() const { return orig; }
vec3 direction() const { return dir; }
point3 at(double t) const {
return orig + t*dir;
}
public:
point3 orig;
vec3 dir;
};
#endif
4.2. 准备一个射线追踪器
我们射线追踪器的核心部分通过像素发送光线,并且计算在这些射线计算这些射线方向上所看到的颜色。所涉及的步骤是:(1)计算光线从眼睛到像素,(2)确定光线相交的对象,(3)计算该交点的颜色因此,需要一个简单的ray_color(ray)函数,它返回背景的颜色(一个简单的渐变)
#include "color.h"
#include "ray.h"
#include "vec3.h"
#include <iostream>
color ray_color(const ray& r) {
vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}
int main() {
// Image
const auto aspect_ratio = 16.0 / 9.0;
const int image_width = 400;
const int image_height = static_cast<int>(image_width / aspect_ratio);
// Camera
auto viewport_height = 2.0;
auto viewport_width = aspect_ratio * viewport_height;
auto focal_length = 1.0;
auto origin = point3(0, 0, 0);
auto horizontal = vec3(viewport_width, 0, 0);
auto vertical = vec3(0, viewport_height, 0);
auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
// Render
std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";
for (int j = image_height-1; j >= 0; --j) {
std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
for (int i = 0; i < image_width; ++i) {
auto u = double(i) / (image_width-1);
auto v = double(j) / (image_height-1);
ray r(origin, lower_left_corner + u*horizontal + v*vertical - origin);
color pixel_color = ray_color(r);
write_color(std::cout, pixel_color);
}
}
std::cerr << "\nDone.\n";
}
这个版本的ray_color(ray)函数将白色和蓝色线性混合。会产生依赖于Y坐标的从蓝到白的渐变
5. 添加球体
我们在我们的光线追踪器中添加一个物体,因为对于光线追踪来说,打球是非常简单的。
5.1. 射线交球
以源点为中心,半径为R的球体方程为
x
2
+
y
2
+
z
2
=
R
2
x^2 + y^2 + z^2 = R^2
x2+y2+z2=R2
其中对于任意的一个点(x, y, z)来说,如果
x
2
+
y
2
+
z
2
>
R
2
x^2 + y^2 + z^2 > R^2
x2+y2+z2>R2
则表明该点在球外,反之则在球内
5.2. 建立我们第一个光线追踪图象
如果我们把这个数学和硬编码应用到我们的程序中,我们就可以用红颜色来测试它,在z轴上我们放置在−1上的任何像素都是这样的:
bool hit_sphere(const point3& center, double radius, const ray& r) {
vec3 oc = r.origin() - center;
auto a = dot(r.direction(), r.direction());
auto b = 2.0 * dot(oc, r.direction());
auto c = dot(oc, oc) - radius*radius;
auto discriminant = b*b - 4*a*c;
return (discriminant > 0);
}
color ray_color(const ray& r) {
if (hit_sphere(point3(0,0,-1), 0.5, r))
return color(1, 0, 0);
vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}
这样我们就得到了一个红色的球体
后面我们会继续讲到对于这个球的阴影,反射,材料等