这其实是我大三上图形学大作业的报告,最近整理本科期间的项目,于是把报告里面比较关于 OptiX 的部分拿出来
水篇博客做个笔记。个人认为 API 没有什么讲的价值,所以博客里更多是讲讲关于 OptiX 的编程模型/思想。
另外有关光追基本知识的部分此处也不过多赘述。
1 基本架构
OptiX 的主要结构如上图所示,由光追渲染管线、加速结构、着色器表及相关资源构成。
值得注意的是,OptiX 其实只负责了渲染的部分,也就是说每次 OptiX 程序运行完成后返回的都是一帧渲染好的图像,而建模、交互,甚至包括画面的显示,都不是 OptiX 所负责的。你可以搭配 OpenGL 之类的来完成这些显示窗口的维护和交互工作。
1.1 光追渲染管线
- 光追渲染管线是整个光追的核心,它负责了光线的发射、相交的检测、每条光线的着色,以及这些全部计算的调度
1.2 加速结构
- 加速结构 (Acceleration Structure) 加速的其实是相交检测,OptiX 中使用加速结构来标记物体的每个面,然后计算的就是每条光线与加速结构的相交情况
- 加速结构本身是一个树形的结构,使用加速结构可以加速光线相交检测的遍历
- 在注册完全部的加速结构后,OptiX 会编译一个查询表,通过这个加速表可以实现非常高效的相交检测
- 但是需要注意的是,当建模发生变化的时候,加速结构就会发生变化,进而导致查询表的重新编译,所以在 OptiX 中,modeling 的变化会使得计算压力增大
1.3 着色器表
- 着色器表 (Shader Table) 中储存了各种与最后的着色有关的资源的索引,可以用于索引一次光线追踪中可能用到的所有资源,比如材质、纹理、法向等,通过着色器表,我们可以快速的获取到这些资源,然后完成相关的计算
每次渲染时,由光追渲染管线调度光线的发射,当光线发生相交后,加速结构给出发生相交处的物体的属性索引,然后我们在着色器表中按照索引查找相关的属性和资源,再交给着色器进行最后的着色,就得到了这个像素上这条光线渲染出来的结果。
2 光追可编程渲染管线
与光栅化一样,光追的渲染管线也是可编程的。光追的渲染管线在前面已经解释,其中可以编程的主要是光线发射、相交检测、着色三个部分。
2.1 光线发射
OptiX 可以实现比较复杂的光线发射,指向针对光源进行重要性采样,不过在我们这儿因为场景足够亮,所以没有做相关的定制。
2.2 相交检测
相交检测背后的算法是二叉树 BVH (Bounding Volume Hierachy),是空间切分技术之一。使用树形组织的加速结构,可以加速相交检测的计算。但是即便如此,依然是一个开销非常大的计算,在没有 RT Core 的传统 GPU 上,只能通过软件模拟的方法实现相关的计算。
而在 RTX GPU 上,这一算法有相关的硬件实现,因此可以得到非常高的运行效率。
在 OptiX 中直接提供的加速结构只有三角网格结构,不过三角网格的建模能力已经非常强了,所以暂时也没有新增其他结构的需求。
2.3 着色
在光线发生与物体的相交后,OptiX 就会调用相关的着色器开始进行颜色的计算。这一部分也是我们对于渲染管线定制最大的部分。
所有的着色器所干的事情,都是根据相交点的相交类型及相关信息,计算该束光应该拥有什么颜色。
2.3.1 最近相交 (Closest Hit)
最近相交,顾名思义就是所有相交中距离相机最近的一个。这样就自然实现了深度检测,而不需要 Z-buffer 之类的辅助。
2.3.2 任意相交 (Any Hit)
与最近相交不同,任意相交就是所有可以与光线发生相交的地方被调用的着色函数。下面的图非常形象的展示了两者的关系,其中上半部分是 Any Hit,下半部分是 Closest Hit。
2.3.3 无相交 (Miss)
假如光线没有与任何加速结构发生相交,那么最后它将打到无穷远处,进而弱到无法获得任何亮度和颜色。我们可以利用这种情况实现天空盒。