接下来我们介绍光线追踪算法,用于实现我们的第一个光线追踪器。
首先,我们可以发现自然实际中光线的传播只是大量从光源发射的无数光线与表面的碰撞交互,直到进入我们眼睛的表面上。光线追踪因此是一个直接基于事实的一个方法。
光线追踪算法获取一个由像素组成的图片,对于每个图片上的像素,它发射许多主光线到场景中,主光线的方向可以通过一条从眼睛到像素中心的方向来获得。在我们拥有主光线集后,我们呢就可以检查场景中的每个物体来判断其是否与物体交叉。在某些例子中,主光线会与超过一个的物体交叉,这是我们会选择一个最靠近眼睛的交叉点。然后我们从交叉点射出阴影光线到光源。如果这一特定的光线不会被路径上的物体遮挡,那么该交叉点就会被照亮,反之就在阴影中。
如果我们对每个像素重复这一过程,我们就会得到三维场景的二维表示。
这一过程我们以伪代码的形式展示:
for(int j = 0; j < imageHeight; j++)
{
for(int i = 0; i < imageWidth; i++)
{
// compute primary ray direction
Ray primRay;
conputePrimRay(i, j, &primRay);
// shoot prim ray in the secne and search for intersection
Point pHit;
Normal nHit;
float minDist = INFINITY;
Object onject = NULL;
for(int k = 0; k < objects.size(); k++)
{
if(Interset(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(Interset(objects.size(), shadowRay))
{
isShadow = true;
break;
}
}
}
if(!isShadow)
{
pixels[i][j] = object->color * light.brightness;
}
else
{
pixels[i][j] = 0;
}
}
}
光线追踪的优势在于,它可以只用很少的代码就可以实现。我们可以大概只用200行代码去实现一个基本的光线追踪器。不同于其它的算法,如扫描线渲染器,光线追踪器实现的难度要小一些。
不过光线追踪虽然很不错,但它的速度是硬伤。首次提出光线追踪技术的Appel在他的论文Some Techniques for Shading Machine Renderings of Solids
中提到过:该方法非常耗时,以一个线框绘制作为基准的话,光线追踪通常要花费其上千倍的时间去计算,其中一半的时间是去决定点到场景中的投影。
光线追踪很慢,找到光线与几何体之间的交叉处这一过程极其的耗时。过去的几十年,该算法的速度一直是主要的短板。然而,计算机发展迅速,这在目前来看只是个很小的问题,不过还是有一点无法改善:对比其它的技术,像深度缓冲这样的算法光线追踪仍很慢。然而,如今,使用,我们更强大的计算机,我们可以花费一个小时或几分钟甚至更少的时间来计算一帧图片。实际上,实时渲染和交互式光线追踪器目前是一个很热门的话题。
总结一下,需要记住渲染过程看上去可以被分为独立的两个过程,其中一步决定一个点是否在一个特定的像素上可见,第二步会在该点着色。不幸的是,两个步骤都需要在进行光线几何体交叉检测时花费大量的时间。不同通过结合新的技术,我们可以对其进行加速。
网友评论