蒙特卡洛光线追踪技术系列 见 蒙特卡洛光线追踪技术
让我们在光线跟踪器中添加一个对象。人们经常在光线跟踪器中使用球体,因为计算光线是否击中球体非常简单。回想一下,以半径R原点为中心的球体的方程是x*x+y*y+z*z=R*R。读取该方程的方法是“对于任何(x,y,z),如果x*x+y*y+z*z=R*R,则(x,y,z)在球体上,否则它不在”。如果球体中心位于(cx,cy,cz),则会变得更丑:
在图形中,你几乎总是希望你的公式是矢量形式的,所以所有的x/y/z都在vec3类中。您可能会注意到,从中心C=(C x,C y,C z)到点p=(x,y,z)的向量是(p-C)。点((p-C),(p-C))=(y-cy)*(y-cy)+(z-cz)*(z-cz)。所以矢量形式的球方程是:
我们可以把它理解为“满足这个方程的任何点p都在球面上”。我们想知道我们的射线p(t)=A+t*B是否曾撞击过球体的任何地方。如果它真的碰到了球,有一些t,p(t)满足球方程。所以我们在寻找任何一个t,如果这是真的:
或展开射线p(t)的完整形式:
向量代数的规则就是我们想要的,如果我们把方程展开,把所有的项移到左边,我们得到:
方程中的向量和R都是常数和已知的。未知的是t,方程是二次方程,就像你在高中数学课上看到的。你可以解t,有一个平方根部分,它要么是正的(意味着两个实解),要么是负的(意味着没有实解),要么是零(意味着一个实解)。在图形学中,代数几乎总是与几何学直接相关。我们所拥有的是:
如果我们把这个数学公式和硬编码输入到程序中,我们可以通过在z轴上的-1处的小球上涂上红色来测试它:
bool hit_sphere(const Vector3& center, float radius, const Ray&r) {
Vector3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0*dot(oc, r.direction());
float c = dot(oc, oc) - radius*radius;
float discriminant = b*b - 4 * a*c;
return (discriminant > 0);
}
Vector3 color(const Ray&r) {
if (hit_sphere(Vector3(0, 0, -1), 0.5, r))return Vector3(1, 0, 0);
Vector3 unit_direction = unitVector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0 - t)*Vector3(1.0, 1.0, 1.0) + t*Vector3(0.5, 0.7, 1.0);
}
........ main函数 ........
for (int j = HEIGHT-1;j >= 0;j--) {
for (int i = 0;i < WIDTH;i++) {
int offset = (WIDTH*j + i) * 4;
float u = float(i) / float(WIDTH);
float v = float(j) / float(HEIGHT);
Ray r(origin, lower_left_corner+u*horizontal+v*vertical);
Vector3 col = color(r);
Pixels[offset + 0] = (unsigned char)255.99*col[0];
Pixels[offset + 1] = (unsigned char)255.99*col[1];
Pixels[offset + 2] = (unsigned char)255.99*col[2];
Pixels[offset + 3] = 255;
}
}
测试结果: