光线追踪算法采用由像素组成的图像。对于图片中的每个像素,它会将主光线射入场景(从眼睛(或相机)射入场景的第一条光线称为主光线、能见度光线或相机光线。)。该主光线的方向是通过追踪从眼睛到该像素中心的线来获得的。一旦我们设置了主光线的方向,我们就会检查场景中的每个对象,看看它是否与其中任何一个相交。在某些情况下,主射线将与多个对象相交。发生这种情况时,我们选择其交点最接近眼睛的物体。然后,我们将阴影光线从交点射到光线上(图1)。
图 1:我们通过像素中心发射主光线,以检查可能的物体交叉点。当我们找到一个点时,我们会投射阴影光线以确定该点是被照亮还是处于阴影中。
如果该光线在到达光线的途中不与物体相交,则命中点将被照亮。如果它确实与另一个对象相交,则该对象会在其上投下阴影(图 2)。
图 2:小球体在大球体上投下阴影。阴影光线在到达光线之前与小球体相交。
如果我们对每个像素重复此操作,我们将获得三维场景的二维表示(图 3)。
图3:为了渲染一帧,我们为帧缓冲区的每个像素拍摄一条主光线。
以下是该算法在伪代码中的实现:
for (int j = 0; j < imageHeight; ++j) {
for (int i = 0; i < imageWidth; ++i) {
// compute primary ray direction
Ray primRay;
computePrimRay(i, j, &primRay);
// shoot prim ray in the scene and search for the intersection
Point pHit;
Normal nHit;
float minDist = INFINITY;
Object object = NULL;
for (int k = 0; k < objects.size(); ++k) {
if (Intersect(objects[k], primRay, &pHit, &nHit)) {
float distance = Distance(eyePosition, pHit);
if (distance < minDistance) {
object = objects[k];
minDistance = distance; //update min distance
}
}
}
if (object != NULL) {
// compute illumination
Ray shadowRay;
shadowRay.direction = lightPosition - pHit;
bool isShadow = false;
for (int k = 0; k < objects.size(); ++k) {
if (Intersect(objects[k], shadowRay)) {
isInShadow = true;
break;
}
}
}
if (!isInShadow)
pixels[i][j] = object->color * light.brightness;
else
pixels[i][j] = 0;
}
}
找到光线和几何体之间的交点非常耗时。几十年来,该算法的速度一直是光线追踪的主要缺点。然而,随着计算机变得越来越快,它越来越不是一个问题。尽管必须说一件事:与其他技术(如 z 缓冲区算法)相比,光线追踪仍然要慢得多。
反射和折射方向都基于交点处的法线和入射光线(主光线)的方向。要计算折射方向,我们还需要指定材料的折射率。
像玻璃球这样的物体同时具有反射性和折射性。我们需要为曲面上的给定点计算两者,但是我们如何混合它们呢?
值的混合取决于主光线(或观察方向)与物体的法线和折射率之间的角度。然而,对我们来说幸运的是,一个方程式可以精确地计算出每种方程式应该如何混合。该方程称为菲涅耳方程。