CUDA光线跟踪渲染器的实现和优化

更新时间:2023-03-19 06:39:01 阅读量: 人文社科 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

中国科技论文在线

CUDA光线跟踪渲染器的实现和优化

黄鑫,戴志涛5 (北京邮电大学计算机学院体系结构中心,北京 100876) 摘要:光线跟踪渲染器能够生成非常接近于现实的图像,然而由于其庞大的计算量,在个人电脑的实时渲染领域应用非常有限。 NVIDIA推出的CUDA平台使得在个人电脑上基于NVIDIA的GPU进行大规模并行计算成为可能。由于光线跟踪算法具有很高的并行特征,本文对传统的光线跟踪渲染器在CUDA上的实现进行了探索,给出了一种完全在GPU端运行的面向对象的实现方案,并对其在CUDA平台上的运行性能进行了优化。

关键词:计算机图形学;光线跟踪;CUDA;面向对象;内存对齐访问

中图分类号:TP391.9

10

15 The Implementation and Optimization of a CUDA-based Ray Tracing Renderer

Huang Xin, Dai Zhitao

(Architecture Center, Computer Science School, Beijing University of Posts and

Telecommunications, Beijing 100876)

Abstract: Ray tracing renderer can generate images that are very close to the reality. However, due to its large amount of computation, real-time rendering in the field of PC applications is very limited. NVIDIA's CUDA platform makes large-scale parallel computing possible on PC based on NVIDIA's GPU. As the ray tracing algorithm has high parallel features, the implementation of traditional ray-tracing renderer on CUDA platform is explored in this paper. An object-oriented implementation that runs entirely on GPU is given, and it's performance on the CUDA platform is optimized.

Key words: Computer Graphics; Ray Tracing; Object-oriented; Coalesced Memeory Access 20 25

0 引言

光线跟踪算法[1] [2]具有非常高的并行特征。对于图像中每个像素点发射的光线的跟踪过30 程,本质上是完全能够独立进行的,不会依赖于其他像素点的计算结果。而光线跟踪的计算

量与生成图像的像素数量成正比。因此,只要能够将光线跟踪算法完全的并行化,就可以大大的提高光线跟踪渲染的速度。例如在3D电影渲染领域,常常使用数百台电脑同时进行渲染。而在个人电脑领域,由于CPU是串行执行指令的,难以实现物理上的并行计算。而如果采用增加CPU的数量方式,CPU之间的协作会变得更加复杂,对软件的要求也会更高。 35 与CPU不同,GPU从一出现开始就以并行计算为核心任务,因此GPU比CPU具有更

强的并行处理性能。但GPU的逻辑控制功能较弱,这就使得GPU长期以来只能进行图形显示的计算。随着个人电脑并行运算需求的增加,显卡厂商开始推出GPGPU的概念,即使用GPU来进行通用计算。但是初期的一些用于通用计算的描述语言需要使用者对GPU的图形渲染管线非常了解[3],这就大大抬高了GPU通用计算的门槛。

40 NVIDIA推出的CUDA平台[4]使得情况有了改观。基于CUDA可以很方便的开发出使

用GPU进行通用计算的程序,并且不需要开发者对显卡的工作原理作过于深入的了解。CUDA提供了对C/C++语言的支持,使得开发过程与基于CPU的开发非常类似,大大简化作者简介:黄鑫(1985-),男,研究生,嵌入式系统与网络通信

通信联系人:戴志涛(1969-),男,教授,嵌入式系统与网络通信. E-mail: daizt@

中国科技论文在线

面向对象的实现方案。

45 了GPGPU的应用。本文对光线跟踪渲染器在CUDA上的实现进行了探索,并给出了一种1 整体设计

在使用GPU进行加速的软件设计中,主要有两种方式。一种方式是将程序的控制逻辑主要由CPU来完成,而只在需要进行数据运算的时候,将需要的数据传入GPU进行计算[5]。而另一种方式则是完全在GPU上进行程序的运行[6] 。这两种方法各有优劣:前一种方式在进行计算前后需要来回传输必要的数据,当数据量较大时,GPU与内存间的传输带宽将成50 为软件性能的主要瓶颈;而在后一种方式中,由于GPU并不适合做逻辑控制,过于复杂的

程序逻辑将使得GPU的并行计算难以发挥。考虑到在光线跟踪渲染器中,场景数据往往较大,并且个人电脑上的光线跟踪应用会更多地关注实时性能,短时间内需要进行多次渲染,若采用第一种方式数据传输量会非常大,因此本文采用了第二种方式来进行设计。

利用面向对象的设计思想,可以很容易设计出光线跟踪渲染器的结构。本文所设计的光55 线跟踪渲染器的主要类图如图1所示。

图1 整体设计类图

在整体设计时,本文考虑了整个渲染器的可扩展性。通过对抽象类Renderer的不同实60 现,外部使用者可以不进行任何修改,仍然通过Renderer类来调用新的渲染方式。而通过

继承Primitive类,并实现其求交点的方法,可以很方便地为该渲染器增加新的图元类型。 2 GPU上的对象数据存放

一般来说,渲染器中的场景数据往往是从外部文件读入到内存中的。为了在GPU上进行光线的跟踪,这些数据需要从内存复制到CUDA的全局内存(Global Memory)中。CUDA65 提供了实现内存复制功能的函数,其函数形式类似于C语言中的malloc和memcpy。然而

在数据复制过程当中,并不能仅仅申请一块与内存中场景数据对象同样大小的全局内存空间,然后执行简单的内存块复制操作。这是因为在面向对象的程序设计中,对象指针的使用是难以避免的。Renderer对象包含指向Scene对象的指针变量。在这种情况下,只将Renderer对象复制到GPU内存中将无法通过该指针变量访问到Scene对象。仅仅将Scene对象也复70 制到GPU内存并不能解决问题,因为Scene对象在GPU内存中的地址将不同于其在内存中

原来的地址,此时Renderer对象中的指针变量指向的将是无效数据(图2)。

图2 将对象数据复制到GPU可能产生的指针问题

75 正确的做法应该是,首先在GPU内存中为Scene对象分配空间,将该对象从内存复制

到GPU内存,然后将Renderer对象中的Scene对象的指针值修改为之前所分配GPU内存的首地址,再用类似的方法把Renderer对象复制到GPU内存中。这样就保证了在GPU端运行代码时能够正确访问到Scene对象的数据。这种方式类似于在内存中的两个对象之间进行深拷贝的方式。本文为每一个需要存放在GPU中的类实现了一个createGpuObject方法,每80 个类的对象在该方法中保证对自身进行正确的复制。

3 GPU光线跟踪实现细节

在传统的基于CPU的光线跟踪渲染器中,对每个像素点处的光线传递进行跟踪可以通过递归的方式来实现。然而在CUDA平台上,由于函数的递归调用不被支持,需要采用其他方式来进行实现光线跟踪。本文给出了一种基于自定义堆栈,通过对光线的状态进行标记85 的循环跟踪方式,实现了对反射和折射光线的跟踪。该方法的主要流程图如图3所示。

图3 GPU光线跟踪流程图

将光线跟踪过程从递归方式变为循环方式后,尽管程序逻辑变得更加复杂,但从效率上90 来看,循环的方式因为减少了函数调用开销,实际上比递归方式具有更高的效率。

从逻辑上来看,光线之间是类似于二叉树的结构。但在进行光线跟踪时,并不适合使用静态二叉树的遍历算法。由于反射光线和折射光线的生成需要父光线与物体的交点信息,每条光线的颜色计算同样也需要交点信息,而求交运算是影响光线跟踪速度最主要的一个因素,因此,光线的生成和颜色的计算应该同时进行。若采用二叉树的方式存储光线之间的关95 系,则还需要再遍历一遍二叉树来计算颜色,造成重复计算交点。另一方面,尽管可以采用

保存交点的方式,但由于二叉树的结构特点,其占用的储存空间随着迭代次数的增加将成指数增长。而采用本文的堆栈方式,既可以不增加求交点的次数,也使得需要的存储空间与迭代次数保持了线性关系。

本文实现的光线跟踪渲染器的效果图如图4所示。

100

图4 渲染效果图 (分辨率 = 800×800,迭代深度 = 5)

4 性能优化

105 尽管光线跟踪渲染器已经能够在CUDA平台上正常运行,但由于CUDA的一些固有特

点,程序还有很大的优化空间。本文主要从数据传输和内存访问两个方面尝试对前文实现的光线跟踪渲染器进行了优化。

4.1 数据传输的影响

尽管采用了在GPU端完全执行程序的方式避免了场景数据的来回传输,但是在光线跟110 踪结束后,图像数据将从CUDA全局内存中被复制到内存中。在实际通过GDI或者OPENGL

方式显示过程中,图像数据将再次被传输到显卡的显存中进行显示。当图像数据很大时,显卡与内存的数据传输速度将严重影响渲染器的运行性能。

实际上,图像数据的显示最终仍然是在显卡上进行的。如果能够将光线跟踪生成的显卡中的图像数据直接在显卡上进行显示,就可以避免图像数据的来回传输。CUDA提供了一115 种基于OPENGL的显卡缓存绑定机制。当使用OPENGL进行渲染时,可以将渲染数据指定

为CUDA全局内存中存放的缓存数据。采用这种方式后,渲染器的渲染速度的确获得了一定的提升(表1)。

表1图像数据存放位置对运行性能的影响

120

4.2 内存访问对齐

前文中实现的光线跟踪渲染器,场景中所有的对象数据均放在了全局内存(Global Memory)中。在CUDA平台上,对全局内存的数据访问必须满足严格的内存对齐和存取顺序,才能一次性获取到每个线程访问的数据,实现完全的并行。若这样的规则不能被满足,125 则会产生内存访问冲突,数据的访问将被串行化,GPU的并行计算性能将不能够完全的发

挥。

在进行光线跟踪的过程中,相邻的一组像素的跟踪计算任务通常被安排给一组并行执行的线程组。这些相邻像素发射的光线大部分时候都会与同一个物体相交,造成线程组在同一时刻访问同一个内存单元。全局内存不支持这样的并行访问,所以实际上这些线程组的运行130 都被串行化了。而另一方面,由于相邻像素可能靠近物体的边界,或者在与同一个物体相交

后,由于反射光线和折射光线的角度不同,最终会与不同的物体进行求交点运算。但不同物体的数据在全局内存中往往是难以满足对齐存放的,并且每个线程访问的物体的数据也是不确定的。在这种情况下,全局内存的存取规则很难被满足。

在CUDA平台中,常量内存(Constant Memory)具有一些全局内存所不具备的特点。常135 量内存配备了Cache,在Cache命中的情况下,数据的存取速度是很快的。同时,常量内存

实现了一种类似广播的机制:当一个线程组同时访问同一个内存地址的数据时,所有线程都能在一个存取周期内取得数据,并且访问该数据的速度类似于访问每个线程单独的寄存器。由此可见,常量内存比全局内存更适合于存放场景数据。通过将场景数据存放在常量内存,渲染器的运行性能有了较大的增加(表2)。

140

表2 内存对齐读取对运行性能的影响

由于常量内存只能通过CPU端调用CUDA内存分配函数进行更改,因此当光线跟踪渲染器在CUDA上运行时,常量内存的数据是无法修改的,这就使得常量内存中存放的场景145 数据只能是静态场景的数据。对于3D漫游类的应用,由于只需要移动摄像机,可以将全部

场景存放在常量内存中;而对于其他的3D应用,则可以将相对固定不变的背景场景存放在常量内存中,而将运动变化的对象数据存入全局内存。

另一方面,除了在读取场景数据时可能遇到内存访问的冲突,在光线跟踪结束后,往全局内存中的图像缓冲区写入像素颜色数据时,同样可能发生冲突。但这种冲突相对于读取数150 据的冲突更容易解决,因为冲突的原因主要是写入地址未对齐。这种情况主要出现在图像缓

存按行存放时,缓存的列数量不是一个线程组线程数量的整数倍。因此从图像第二行开始,每次的数据写入可能都是未对齐的。一种比较简单的解决方案是将图像缓存的列数量补齐为线程数量的整数倍。在输出的时候,补齐的区域可以不进行输出(表3)。

155

表3 内存对齐写入对运行性能的影响

5 结论

本文给出了一种基于CUDA的光线跟踪渲染器的面向对象的实现方案,并对其运行性能进行了初步的优化。CUDA提供了对面向对象的C++语言的支持,但对其某些特性的支160 持并不完善,这就使得面向对象程序向CUDA平台的移植存在一定的困难。另一方面,为

了充分利用GPU的并行计算能力,开发人员还需要对CUDA的编程模型和内存访问机制足够的了解,以避免内存冲突和数据传输等因素对程序运行性能造成影响。CUDA还有非常多的特点本文并没有涉及,利用这些特点,本文的光线跟踪渲染器还可以进一步的改进和优化。接下来的研究工作主要关注如何对CUDA上的光线跟踪渲染器进行更深入的优化,以165 探索光线跟踪在个人电脑上进行交互式运行的潜力。

[参考文献] (References)

[1] Turner Whitted. An improved illumination model for shaded display. Commun. ACM, 23 (6):343-349,1980

[2] Glassner,Andrew. An Introduction to Ray Tracing[M]. Great Britain: Morgan Kaufmann, 1989

[3] NVIDIA Corporation. Cuda Programming Guide Version 3.1 [M], 2010.5.28.

[4] Christen M.Ray Tracing on GPU:[D]. Switzerland:Fachhochschule beider Basel,2005,12-36

[5] N. Carr, J. Hall, and J. Hart. The Ray Engine[J]. In Proceedings of the ACM SIGGRAPH /EUROGRAPHICS conference on Graphics Hardware,pages 37-46, 2002.

[6] T. J. Purcell. Ray Tracing on a Stream Processor[D]. Stanford : Standford University, 2004.

170 175

本文来源:https://www.bwwdw.com/article/y42j.html

Top