在光线追踪中添加一个物体。人们常常在光线追踪中使用球体,因为计算光线是否击中球体相对简单。
5.1 光线与球体的相交计算
一个以原点为中心、半径为r的球体的数学方程是一个重要的数学方程:
您可以这样理解:如果一个给定的点 (x, y, z) 在球体上,那么它满足方程
如果一个给定的点 (x, y, z) 在球体内部,那么它满足不等式
如果一个给定的点 (x, y, z) 在球体外部,那么它满足不等式
如果我们想让球体的中心可以位于任意点(Cx, Cy, Cz),那么这个方程将变得更为复杂:
(x−Cx)²+(y−Cy)²+(z−Cz)²=r²
在图形学中,你几乎总是希望将公式表达为向量的形式,这样所有涉及 x、y、z 的部分都可以简单地用一个 vec3 类来表示。你可能已经注意到,从中心 C=(Cx, Cy, Cz) 到点 P=(x, y, z) 的向量可以表示为 (P−C)。如果我们使用点积的定义:
(P−C)*(P−C)=(x−Cx)²+(y−Cy)²+(z−Cz)²
那么我们可以将球体的方程以向量形式重新写成:
(P−C)*(P−C)=r²
我们可以将其理解为“满足这个方程的任意点 P 都在球体上”。我们想知道我们的光线 P(t)=A+tb 是否会击中球体的某个位置。如果它确实击中了球体,则存在某个t值,使得 P(t) 满足球体方程。因此,我们正在寻找任何满足这个条件的 t 值:
(P(t)−C)*(P(t)−C)=r²
可以通过将 P(t) 的扩展形式代入来找到该值:
((A+tb)−C)*((A+tb)−C)=r²
在左边我们有三个向量点乘右边的三个向量。如果我们解出全部的点乘积,会得到九个向量。你可以逐项列出并计算,但我们没必要这么费力。如果记得的话,我们想要解出 t 的值,因此我们将项分为是否包含t:
(tb+(A−C))*(tb+(A−C))=r²
现在我们按照向量代数的规则来分配点乘运算:
t²b*b + 2tb*(A−C) + (A−C)*(A−C) = r²
将半径的平方移到左边:
t²b*b + 2tb*(A−C) + (A−C)*(A−C) − r² = 0
很难确定这个方程的具体含义,但是方程中的向量和半径 r 都是常量并已知。此外,我们通过点积将所有向量化简为标量。唯一的未知数是 t,并且我们有一个 t²,这意味着这个方程是二次方程。可以使用二次方程公式来解决二次方程:
对于光线和球体的相交,a、b、c的值是:
a = b*b
b = 2b*(A - C)
c = (A - C) * (A - C) - r²
利用上述所有的内容,你可以解出t,但是有一个平方根部分可能是正(表示有两个实数解),负(表示没有实数解)或者零(表示有一个实数解)。在图形学中,代数几乎总是与几何直接相关的。我们有以下内容:
5.2 创建我们的第一张光线追踪图像
如果我们将这个数学公式硬编码到我们的程序中,我们可以通过在z轴上将一个小球放置在-1位,并将与其相交的像素块标记为红色来测试我们的代码。
#include "color.h"
#include "ray.h"
#include "vec3.h"
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 a = 0.5*(unit_direction.y() + 1.0);
return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}
运行得到:
现在这个场景缺少了很多东西,比如阴影、反射光线和多个物体,但我们离目标完成的一半要比刚开始时更接近!需要注意的一点是,我们正在测试光线是否与球体相交,通过求解二次方程并判断是否存在解来进行判断,但含有负值的解同样完全有效。如果您将球体的中心位置改为 z=+1,您将得到完全相同的图像,因为该解并不能区分相机前方的物体和相机后方的物体。这并不是我们想要的结果!接下来我们将修复这些问题。