基于光线追踪的渲染算法实现

描述

本文翻译自Scratchapixel 3.0[1],是一个关于计算机图形学的系统性的学习教程。如果有误,欢迎在评论区讨论。

我们已经涵盖了所有需要说的内容!我们现在准备写我们的第一个光线追踪器。你现在应该能够猜到光线追踪算法是如何工作的了。
 

首先,注意到自然界中光的传播只是从光源发出无数条射线,反弹直到它们撞到我们眼睛的表面。因此,光线追踪是优雅的,因为它直接基于我们周围发生的事情。除了它按照相反的顺序跟随光线的路径外,它是一个完美的自然模拟器。光线追踪算法使用由像素组成的图像。对于图片中的每个像素,它向场景中发射一个主射线。该主射线的方向是通过从眼睛到该像素中心的线追踪得到的。一旦我们设置了该主射线的方向,我们检查场景中的每个对象,看它们是否与任何对象相交。在某些情况下,主射线将与多个对象相交。当这种情况发生时,我们选择离眼睛最近的交点所在的对象。然后,我们从交点向光源发射一个阴影射线(图 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; }}

正如我们所看到的,光线追踪的美妙之处在于它只需要几行代码;一个基本的光线追踪器只需要 200 行代码。与其他算法(如扫描线渲染器)不同,光线追踪的实现需要很少的努力。Arthur Appel 在 1969 年的一篇名为“一些用于给固体着色的机器渲染技术”的论文中首次描述了这种技术。那么,如果这个算法如此出色,为什么它没有取代所有其他渲染算法呢?主要原因在于速度,当时(甚至今天在某种程度上)是这样的。正如 Appel 在他的论文中所提到的:

“这种方法非常耗时,通常需要比线框图绘制多几千倍的计算时间才能获得有益的结果。其中约有一半时间用于确定投影和场景之间的点对点对应关系。”

换句话说,它很慢(正如 Kajiya 所说的,他是所有计算机图形学历史上最有影响力的研究人员之一:“光线追踪不慢,计算机慢”)。查找光线和几何图形之间的交点非常耗时。几十年来,算法的速度一直是光线追踪的主要缺点。然而,随着计算机变得更快,它越来越不是问题。尽管仍有一件事必须说:与其他技术(如 z 缓冲算法)相比,光线追踪仍然要慢得多。然而,随着今天的快速计算机,我们可以在几分钟内计算出以前需要一个小时才能完成的帧。实时和交互式光线追踪器是一个热门话题。

总之,重要的是要记住,渲染例程可以被认为是两个单独的过程。一步确定一个物体表面上的点是否从特定的像素可见(可见性部分),第二步着色该点(着色部分)。不幸的是,这两个步骤都需要昂贵和耗时的光线-几何交点测试。这个算法是优雅而强大的,但是它迫使我们在渲染时间和精度之间进行权衡。自 Appel 发表论文以来,已经进行了大量研究来加速光线-物体交点例程。随着计算机变得更加强大并结合这些加速技术,光线追踪成为了日常生产环境中可用的方法,并且是大多数渲染离线软件所使用的事实标准。视频游戏引擎仍在使用光栅化算法。然而,随着 GPU 加速光线追踪技术(2017-2018)和 RTX 技术的最近出现,实时光线追踪也在可及范围内。虽然一些视频游戏已经提供了可以打开光线追踪的模式,但仅限于简单的效果,如清晰的反射和阴影。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分