![efef29d8877874cecfcd4d77c497744e.png](https://img-blog.csdnimg.cn/img_convert/efef29d8877874cecfcd4d77c497744e.png)
注:感谢各位前辈指正!文章里bug还蛮多的哈哈哈,详见评论区啦!
过了俩月:David Kuri大佬终于在领英上回我了 我好了。但是回头看看这篇真的好多错啊啊啊而且当时问的是在油管上发教程的许可…又有一大堆事情可以肝了…
本文章中结合光线追踪和立体渲染的算法来自于本人的本科毕业设计,参考代码架构来源及资源引用详见下文。因为涉及一些必要背景知识的介绍,文中包含大量外部链接。转载请注明出处以及尽量保留原格式。
引言和声明
我对于使用Unity进行实时光线追踪渲染的最初了解来自于 @IceDust 前辈的这篇文章:
IceDust:从零开始的简单光线追踪示例zhuanlan.zhihu.com![f42ccb9af7ab1f1ceac091f88f862f88.png](https://img-blog.csdnimg.cn/img_convert/f42ccb9af7ab1f1ceac091f88f862f88.png)
我被其中令人惊艳的渲染效果深深吸引了,并且希望自己能复现相应的效果,从而对这一方向有更深入的了解。为了验证自己的一些想法,我对IceDust前辈在私信中进行了提问,也得到了前辈详尽的回答,在此我十分感谢IceDust前辈对我在实时光线追踪领域的启蒙和指导。
虽然我在今年6月份就完成了这一毕业设计项目的渲染,但是因为实现光线追踪部分的算法和数据结构来源于一篇英文博客:
GPU Ray Tracing in Unity - Part 1blog.three-eyed-games.com![d453ef22e9d16756a9140203755b6032.png](https://img-blog.csdnimg.cn/img_convert/d453ef22e9d16756a9140203755b6032.png)
我以这篇博客的光线追踪代码架构为基础,加入了立体渲染的部分,这也是本文章核心介绍的部分。虽然博客作者David Kuri也公开了他的全部代码,但是我还是在博客留言和领英connect申请他对于我在其他网站公开发表我的渲染结果和技术思路的许可。可能因为作者工作繁忙,我在这两边都一直没有得到答复,留言提交后也没有在博客上得以显示。
因为下周我马上就要开始研究生项目的学习,所以我决定先进行这篇文章的整理和发布,作为对于本科学习的总结,并以此开展新的学习和生活。如果我后续得到了作者答复,会再根据答复对这篇文章进行处理或编辑。
项目中使用的背景图片来源网站在David Kuri的博客中,同样是他使用的图片(在复现时便于进行对比),因为效果很好所以就保留下来了,没有更换其他图片。
本项目没有使用RTX显卡,使用的是unity支持的compute shader这一着色器进行屏幕渲染结果绘制,版本2018.2。因为本文旨在进行一个最终实现成果的分享,所以对于自己在项目中走的弯路,实现的其他效果和关于unity shader和compute shader渲染效率的对比就不在此展示了。
尽管我只在这个方向上完成了一点点值得我为之自豪的工作,我还是希望把我的学习成功分享给更多人,其中有很多不成熟的地方,新的整合算法也有未能实现的功能,还请大家多多包涵。因为我写作能力尚不成熟,文章也篇幅有限,更不可能对于每一项引用的内容都进行翻译和解释,所以肯定有说明不够详尽的地方。文章中也有很多对于渲染和仿真的主观描述的句子,总之这并不是一篇展示代码和数据结构的文章,而是倾向于展现一种渲染思路的可能。如果你对这篇文章中的内容或是其他相关内容感兴趣,欢迎留言和我或是其他前辈们进行探讨。
如果这篇文章引起了足够多的共鸣,我可能会发布更详细的教程或整理方便大家复现的工程。我之所以选择在知乎这个社区做这件事情,是因为我关于渲染的一切,都从这里开始。
特别鸣谢在项目期间为我提供无私指导的阿立老师及不断鼓励我的家人和朋友们。
背景知识
光线追踪这一概念在很久之前就被提出了,但是在从零开始了解这一算法的时候,很容易遇到一个影响理解的问题:因为缺乏翻译规范和作者本身用词不够严谨,对于光线追踪这一名词的使用可能包含至少两个意思——“一种强调镜面反射的渲染效果”或是“通过屏幕像素发射射线对场景进行探测的渲染算法”。以下简称为“光线追踪效果”和“光线追踪算法”。
“光线追踪效果”很常见,也是RTX显卡demo中的常客,可以说绝大多数使用RTX显卡制作的demo都在强调通过镜面反射实现的更有真实感的渲染效果:地面上的积水,车辆表面的反光等。但也正是因为对于这一效果的过高关注,使得人们在提到“光线追踪”时,会更关注它渲染结果层面的含义,而淡化算法层面的含义。
![adfe1644c6551cf418438582bb423735.png](https://img-blog.csdnimg.cn/img_convert/adfe1644c6551cf418438582bb423735.png)
“光线追踪算法”的提出可以追溯至1968年的论文“Some techniques for shading machine renderings of solids”,作者Arthur Appel。如果查阅原版论文,会发现一个手写的副标题:ray tracing (casting) algorithm。
![7d00b12635d9aa8bacf8fbbaeabef09f.png](https://img-blog.csdnimg.cn/img_convert/7d00b12635d9aa8bacf8fbbaeabef09f.png)
这个副标题中提到了两个词:ray tracing和ray casting。Ray tracing自然指的是我们熟悉的“光线追踪”(不论是算法还是效果,仅从英文字面翻译而言。虽然还有一个对应的英文叫ray marching……甚至更多名称)那这里的ray casting又是什么?
仅根据英文字面翻译的话,我认为ray casting应该意为光线构造。但是这个名称显然不够确切,在实际的应用背景下,ray casting实际的含义应对应为立体渲染(又名volume rendering,及其他相关词的替换),具体的应用方向包括但不限于医疗可视化和体积光绘制。但令初学者困惑的是,在很多立体渲染相关的项目中,作者依然会使用光线追踪(ray tracing)作为核心词或标题,这完全没有问题,因为这里的光线追踪实际是指“光线追踪算法”,而非那个亮晶晶的镜面反射渲染效果。
一个使用unity进行医疗可视化的例子:
Unity® Volume Rendering - Unity Connectconnect.unity.com![a29d9aa681a064ecd42f0bad0794a3f3.png](https://img-blog.csdnimg.cn/img_convert/a29d9aa681a064ecd42f0bad0794a3f3.png)
一个unity商城中的体积光绘制插件:
Aura 2 - Volumetric Lighting & Fog - Asset Storeassetstore.unity.com![84b1d4fa68e9660c128e92e5ed596b56.png](https://img-blog.csdnimg.cn/img_convert/84b1d4fa68e9660c128e92e5ed596b56.png)
在此,为了避免产生进一步的混乱,我们需要规定一下本文章中对于这些概念唯一确定的名称,并尽量简短的总结一下以上的内容:
有一种“从屏幕像素发射射线探测场景”的算法叫做光线追踪。
光线追踪包含两种主要的渲染效果:强调镜面反射(亮晶晶)的ray tracing和强调细节展示(雾蒙蒙)的ray casting。
这两种渲染效果具体怎么区分请见这个视频:
https://www.youtube.com/watch?v=ll4_79zKapUwww.youtube.com对于无法观看这个视频的人们,我在这里简单说一下我的概括:
屏幕像素发出射线,碰到模型射线方向改变了,不考虑模型内部信息的,是ray tracing,为更真实的画面效果服务。
屏幕像素发出射线,碰到模型射线方向不改变,考虑模型内部信息的,是ray casting,为展现更多细节服务。
至于根据这个分类方法,IceDust前辈实现的折射效果算是那边呢……因为光线在接触到表面之后还是发生了方向的改变,所以我认为是tracing的一种。因为我在项目中没有来得及实现折射的部分,所以也不过多讨论了,希望了解折射渲染效果的请直接去看IceDust前辈的文章吧。
整合方法和相关猜想
在前文中我们主要规定了三个名称:光线追踪算法,和它的两种主要渲染结果ray tracing和ray casting。示意图和效果如下:
![c95d2e06b8b99347ea01603db4afdcbb.png](https://img-blog.csdnimg.cn/img_convert/c95d2e06b8b99347ea01603db4afdcbb.png)
![556165f948f0498c586148673f3dfd36.png](https://img-blog.csdnimg.cn/img_convert/556165f948f0498c586148673f3dfd36.png)
那有没有可能在一个场景中同时实现ray tracing和ray casting的渲染效果呢?因为每一次光线追踪算法的结果都是一个像素的RGB颜色(就像人眼底实际看到的一个颜色),所以当然是可以将两种渲染效果进行整合的,因为这就是在真实世界时刻发生的。具体的整合算法并不难理解,我受到传统渲染管线中对于透明物体绘制顺序的启发,采用了如下方案:
![4fee5117c2a65295cc1c49d26eb45568.png](https://img-blog.csdnimg.cn/img_convert/4fee5117c2a65295cc1c49d26eb45568.png)
在传统渲染管线中绘制透明物体时,要先绘制全部不透明的物体,最后再根据z轴深度判断透明物体是否能得以显示,再在绘制好的不透明物体的底色上进行透明颜色叠加。
而对于光线追踪算法来说,ray tracing的部分因为不涉及模型内部信息,所以可以认为是对应了不透明物体,而ray casting的部分刚好对应着透明物体,这一问题也就迎刃而解了。
此时我们再看我实现的这个封面的渲染效果:
![66ecc5cda3010850dbda139a183ebf80.png](https://img-blog.csdnimg.cn/img_convert/66ecc5cda3010850dbda139a183ebf80.png)
其中“金属球”的部分就是ray tracing的部分,反射着天空盒和彩虹体数据,彩虹的部分是提前生成好的,存储在Texture3D中的体数据,再将这个体数据对应到一个cube上,就可以进行立体渲染了。
更多的细节和实时渲染效果请见这个视频:
![f3aac14ef1c9dfb54333abb34fe60cb0.png](https://img-blog.csdnimg.cn/img_convert/f3aac14ef1c9dfb54333abb34fe60cb0.png)
目前这个整合算法包括的问题有没有实现折射,对于三角面模型求交效率极低(无法进一步优化),没有进行光线分配和合并(例如在彩虹体数据中应该同时看到反射效果和彩虹本身的颜色,但是在当前算法中彩虹的本身颜色被忽略了),只能对一个体数据块进行渲染等。并且,这个整合算法究竟有什么意义呢?
![1453a62f4b3a66f9e8446e7113c819e7.png](https://img-blog.csdnimg.cn/img_convert/1453a62f4b3a66f9e8446e7113c819e7.png)
虽然看起来只是把一些最好求交的球和已经生成好的体数据放在一起,搭建了一个好看但是没有实用价值的小demo场景,但这个整合的行为实际上是对于更真实渲染效果的一次试探和挑战,还是那句话,因为现实就是如此运作的。我在项目中还实现了可以实时计算体数据的渲染,进行三维场强的绘制(包括物体对于信号的反射)。具体如下,但是结果并不令人满意,并且因为这个项目的渲染完全依赖compute shader中代码的控制(可能是因为编译顺序不同,无法在运行时对compute shader中代码进行if/else判断并分流),所以想要对比不同的渲染结果(例如视频后半部分的仅显示反射信号强度),需要修改代码并重新编译:
![bb2284a93abc15dad11ee5ebe84ee3fa.png](https://img-blog.csdnimg.cn/img_convert/bb2284a93abc15dad11ee5ebe84ee3fa.png)
总之这是一个非常青涩的光线追踪渲染项目,但对于我在这个项目中所作的尝试,我认为这不仅仅是一个简单的算法整合工作,而是一个对于现有渲染管线进行全新变革的可能,是一个逼近更真实渲染效果的可能。光线追踪算法中最大的消耗量在于求交运算(RTX显卡也正是因为内置了求交运算硬件模块使得对于更复杂场景的追踪成为可能),假设能实现足够快的运算方式,使得场景中的物体既拥有充实的内部细节,又可以将这些“点云”连结成正确的表面信息,那么计算机图形学对于现实的仿真(甚至可以说是还原)将无限接近于真实。举例来说,在对于一段树干或是一块岩石的模型进行破坏时,可以不断地剥落表面而展现出内部的细节,这将是多么美妙的画面。场景不再像三维mesh投影二维的方式一样,大部分是空的,而是大部分的满的,此时模型的存储结构和渲染管线将会与现在(mesh类)大不相同,世界将为之变革。这种新的渲染方式大概是我研究生的课题方向,也可能是我要为之付出多年行动的梦想。
结语
至此我的第一篇知乎文章就告一段落了,主要完成的是一个思路的分享,具体的技术细节太过琐碎,不在此逐一说明。欢迎各位前辈在评论中对我进行指导或一起进行讨论。今后如果我还有值得分享的东西,我也会继续整理并发布在这里,希望这些文字和图片,在某一天能对某个读者产生一点独特的激励和影响。