|
2020年12月8日,由腾讯游戏学院举办的第四届腾讯游戏开发者大会(Tencent Game Developers Conference,简称TGDC)进入了第二天的议程。来自腾讯NExT Studios工作室的高级技术美术师谢渊分享了NExT工作室在光线追踪技术探索与运用上的思路。以下是文字实录:
大家好,我是来自于NExT工作室的谢渊,今天有机会跟大家一起聊一聊光线追踪。
真实世界的成像
首先在我们了解光线追踪之前,我们看看真实的世界它的光照是如何进行的。
我们现在看到的这张图,通常我相信大家应该不会用人眼直接去看太阳吧?或者去看一些光源。大多数的时候,光照直接从窗户、门直射进来,进来之后经过墙壁或者物体,进行一些二次反弹、三次反弹,逐渐随着光的能量的衰减,我们大多数时候看到的是这样一副图像。
从微观的角度,我们看一看光线是如何运作的。假设一束光打到一个平面上,这时候的平面,在自然界的视角里,当我们把它无限放大以后会发现,它的表面并不是那么的完美,那么平整,所以说它的表面上会有很多小的起伏,我们称之为微表面。
光线打到这个微表面上面以后,又会随着这个凹凸不平反射到不同的方向。在用电脑进行模拟自然界光线的时候,比如说我们现在看到的上方这个图。
这是一个很简单的场景,一个很小的屋子里面,上面有一盏灯。那么这盏灯它的光源发散的方向是四面八方的,我们可以想象出每一束光线打到周围的墙壁上之后,又随着墙表面凹凸不平,又发散出更多的一些二次反弹,那二次反弹之后又打到一些桌子上、地面上、房顶上,每接触到一个平面,又进行了一些更多次的反弹。
可以想象,这样的一个运算量,其实是非常巨大的。所以通常我们在模拟这样的一个光照情况下的时候,之前都通过一个离线渲染的方式来呈现出来的。
光栅化渲染
现在我们再想想,如果我们再实时渲染领域,那么如何去呈现这样的一个自然情况呢?这时候我们要引入一个概念——光栅化。
什么是光栅化?当我们眼睛看到一个屏幕之后,我们所关注的所有的内容是在这个屏幕内的这些物体,所以这个时候,我们会以屏幕空间做一个裁切。
也就是屏幕外的东西,我们暂时先不考虑到它的一些计算,然后,我们把屏幕上的这些三维物体的信息投射到屏幕的每一个网络像素上。同时在整个三位场景里面,把转换到屏幕上的这些信息进行分类。比如说有一些法线信息、有一些深度信息、有一些ID信息,诸如此类等等。
然后最后当我们渲染的时候,我们会调用这些储存到显存里面的不同信息,进行一个合成运算,那这样最后就是我们看到的,最终的一个画面效果。
通过刚才的一个简单的描述,我想大家应该可以理解到,这种方式它最大的优势是在于快,非常快。因为它会刨去掉很多看不见的一些信息,比如屏幕外的信息统统不算;第二个呢,它会把很多三维信息转换成以屏幕像素的形式作为计算,那这样就大大节省了很多运算量。
不过从另一个角度去看,这样的方式它已经大大地削弱了一些信息。所以很多基于物理上面的一些渲染,或者说一些很好的效果,比如说屏幕外的一些反射等等,在光栅化的渲染下面,并不能很好地呈现。
光线追踪细节的表现
现在我们可以看一下《重生边缘》这个项目。我们可以看到光线追踪开与关的区别。那么右上角大家看到这个绿色LOGO,RTX ON的时候代表光线追踪此时已经被打开了,RTX OFF则代表光线追踪此时已经被关闭。
目前我们可以看到此时此刻RTX,就是光追已经关掉了。那么在RT关掉的时候,它地面这个水潭上面的反射的信息大家可以看到,由于受到屏幕空间的限制,它反射的信息量非常少,屏幕外很多的信息并不能正确地反映过来。
当我们在RTX ON的时候,就反射到很多屏幕外的一些信息。这块大家看到的是一个铁丝网的一个投影,在光追下面的一个投影的效果,这个投影它来自于我们铁丝网是采用Alpha test贴图进行制作的,所以说在光追开的时候,随着光源与被投射物体的距离的近和远,它有一个半影的效果。
当我们在光追关掉的时候,会发现此时的这个投影是清晰的,但是这样对于我们在自然界观察的物体来讲的话,其实并不是很正确。因为随着物体投影距离的长和短,它的影子会有一个逐渐扩散的一个半影效果,这就是非RT情况下面看起来不真实的一个表现。
这里也是一个水潭,它也是在一个阴影下面的。我们通过水潭里面的反射,也会看到天空上面的一些信息,哪怕这个天空它并不在我们屏幕内显示。
反之,关掉以后是乌黑一片,所以说这就是光线追踪带来的一个视觉上比较大的一些差异性。
通常我们在设计一些光线追踪场景的时候,也会考虑到有一些比如说夜晚,或者一些反射金属物体比较多的这张情况,因为这也是比较容易去展现光线追踪的。
这是角色在一个动态灯的一个投影情况下面的阴影效果,此时RT是关着的,所以你会发现它的影子就是从近处到远处一直都是比较实的、比较锐利的。
反之,但RT开了以后,它有一个半影的效果,动态物体的这么一个半影效果,是非常明显的。大家可以考量一下自己平时在路灯下走路的时候,你发现自己的影子是怎么样的效果。
所以整个场景里我们尽可能用了一些动态灯,然后动态物体,然后反射,更多地去营造光线追踪,呈现出来的一些特有的效果。
光线追踪渲染
通过刚才那个视频,我们看到了RT在实际游戏项目中的一些呈现。那这个时候,我们就要回到我们一开始的话题,就是到底光线追踪是如何去渲染的。
在一开篇的时候,我提到了我们自然界的光线都是从光源出发,对所遇到的物体进行一次二次的这样的反弹。那么光追恰恰相反,它是从我们的摄像机作为一个射线投射出来,当这个摄像机如果在我们的三维场景里面,求交了某一个物体的一个面,那么相应它也会发生这样的一些反弹。
经历多次的反弹以后,可以追溯到这个物体、乃至光源,然后依次把这样的信息再反弹到我们的眼睛当中,那样就是形成了整个光线追踪的一个路径的循环。可以知道光线追踪它是有反弹的这么一个概念在里面的,所以大家看看现在右边的这张图。
当我们把反弹的次数逐次提高以后,我们会发现每增加一次,它反弹的次数和数量都得到了巨大的提升和变化。所以说在实时渲染领域,尽管我们已经做了从屏幕出发的光追渲染方式,但是它的计算量依旧是比较巨大的。
这也就是为什么光线追踪在实时渲染领域迟迟不能落地,迟迟不能在真正的项目中进行大批量推广的一个原因。
GPU提升带来了技术革新
现在我们可以看看这张图,这个是近30年来CPU和GPU整个发展的路径。下方这个蓝色的线是CPU性能的一个提升,上方绿色的是显卡GPU的一个提升,通过这张图我们很明显地看到我们GPU发展的速度远远超过了CPU提升的这个幅度。
特别是在2018年的时候,微软发布了DXR,一个基于Directx的RT的这么一个API结构,同年Nvidia也推出了RTX显卡,我们之前的显卡一直是GTX,2018年的时候终于出现了RTX显卡。
那这个显卡它和以往的显卡有什么区别呢?它采用了一个基于光线追踪硬件加速的这么一个内核,所以说从RTX显卡开始,我们可以真正地把光线追踪应用到实时渲染领域。
自从光线追踪在实时渲染领域应用以后,我们就可以在它上面应用一些原来基于一些离线渲染,物理的一些折射、反射、焦散的效果。
在我们实际的项目当中,就是光线追踪它通常是由四种形式得以呈现的,一个就是光追的AO,包括它的阴影,包括它的反射,以及它的Global Illumination,就是GI。
光线追踪的优势
我们逐个了解一下,光追在游戏当中究竟能给我们带来些什么。
这是一张截自于一个游戏画面的截图,我们可以关注一下绿色箭头所指的方向,这都是一个水潭和一个镜子,我们想看一下但我们光追开了以后的变化。
大家可以比较容易地去发现,当它左侧的黄色栏杆在非RT的情况下并没有比较准确、完整地把它渲染出来,包括下方的水潭,其实它的上方有一个黄色的广告牌,但是在屏幕空间反射的时候,因为没有看得到这个广告牌,所以说也就没有办法正确地反射出它在水面的倒影。
[ 光追没开之前 ]
[ 光追开了以后 ]
相对RT这块,就是可以呈现出很多的细节,包括一些准确的位置。
这也是我们通常在游戏开发里面会遇到的一个情景,就是我们会在屏幕空间反射之外,有的时候我们会加一个反射球Reflection Capture,那这个东西,就中间那个白色的球,它主要的目的就是以它所在的位置中心,在它的这个半径范围内会抓取一些当前三维场景的一些信息,并烘焙到一个图上面。
通过这种形式,去弥补一些屏幕空间反射所不能呈现出来的一些信息,但是它的劣势也显而易见。
第一,由于本身它计算的精度问题,它并不能非常准确地呈现出一个反射的位置;第二本身这张图它的尺寸由于也是受限,为了尽可能地去减少显存的一个开销,所以说它的反射出来的内容也是比较模糊的。
同时,当我们在一个游戏场景中大批量采用这样的技术的时候,对显卡的显存性能开销也是比较巨大的。而我们如果在这样的一个开阔场景中,我们采用了一个RT的反射,这样的话,我们看到的内容无论是准确性,包括清晰度都大大的改变,而且也会减少一些显存的开销。
下面这张图是一个动态物体的反射展示。
我们知道动态角色在游戏中他们的一些反射,之前提到Reflection Capture是没有办法的,因为它只能抓取一些静态的物体,所以说在动态物体的时候,只能是一些屏幕空间的反射来支持它的一些比如像现在水面上的一些效果。
那么我们看一下RT开了以后,在动态物体移动的时候,它的投影反射依然是比较清晰可见的。
下面这张图,我们可以看一下,当整个海量AI的这么一个场景呈现在我们视觉当中的时候,角色身上这些材质的Roughness它是相同的,那这样的话我们可以进行一个渲染的合批,在这么多角色被RT的反射渲染下,我们依然可以达到50帧。
所以说这一块优化性能也是很有帮助,与我们在整个大场景的一些渲染。
下面这张图大家不知道有没有发现什么问题,这是一个在屏幕空间AO的一个效果,当我们的视角由俯视变视到平视的时候,它的这个柱子的AO会发生一个很奇怪的现象。
那么为什么会产生这样的效果,我们来分析一下。
当我们比较俯视的时候,会发现柱子边上的这个AO它的屏幕空间产生的AO的原理其实基于一个屏幕深度图的缓存计算,也就是说它会比对当前物体前后的像素,谁深度更深一点,当两者的深度差值如果小于AO的采样半径的时候,那就会被认为前方物体对后方物体产生了一个环境的闭塞,产生了AO。
那么正因为我们是俯视的角度,所以说这样的话,它的深度的差值的对比也是比较小的,那这样电脑就会误认为是这个杯子离这个地面比较近,从而产生了这么一个错误的效果。
如果我们把它的视角稍微平视一点,这样你就会发现它的这个出错的现象会稍微好一些。这其实也是因为当这个角度看上去,它的深度的差异性会比较大,当它超出了我们的这个采样半径以后,相对来讲这样的一个被误认为遮挡的AO关系就随之而减少。
这个就一目了然了,这就是光线追踪的AO,它基于物理运算,你会发现它呈现的AO的效果是非常正常的。
下面我们来聊一聊光线追踪的阴影。
现在在光线追踪被关掉的情况下面,角色它的投影来自于一盏光,这盏光我们采用的是CSM的一个投影,我们把contact shadow其实已经开了,但尽管如此我们会发现在他的衣领上,包括他的衣服上面,依然就会没有明暗关系,显得这个衣服是浮在人身上的,包括眼镜周围都没有一些立体的这张感觉。
这是一个光追开了以后的效果,首先他的这个衣服和身上的内衣之间的半影效果非常真实,包括他的颈部的衣服,尽管它的厚度很薄,但我们依然很清晰地会看到它的一些投影,投在颈椎上面,包括整个眼镜这一块儿,都显得非常自然,接近于我们真实的效果。
这就是基于光追的一个阴影所呈现出来的投影和传统投影之间的一个区别。
这块我们介绍一下光追的一个GI。
目前就是一个简单的小房子,里面放了一个绿色的球,目前这个效果就是我们没有开RT,那整个场景里面它只有两盏光,一个是sky light的天光,包括一个directional light的直射光。
通过这张图,我们可以看到整个室内的效果是很平的,因为它的漫反射并不能很好地被遮蔽,所以这样看起来的这个场景就会显得比较缺乏体积感,会比较平一些。
这是开启的一个RTGI的效果,和刚才这张图大家可以看一下,我觉得是天壤之别。所以有GI和没GI差了很多,那么GI这张图我们可以切换到灯光模式下去看一看。
首先,它在室内会产生一个漫反射的遮蔽,那提及感瞬间就出来了。然后同时我们中间这个球之前是有绿色的,光追经过它的一次反弹、二次反弹之后,它会把球上面的一些颜色带到周围的一些墙壁或者地面,甚至天花板上面。
那右下角的那个参数大家可以看一下,我们现在使用的这个Bounce也就是它的反弹,其实是两次,两次已经可以达到这样的效果了。
这块我要说一下,我们在实际项目中并没有采用RTGI。
第一个原因就是我们整个开发的团队自己本身有一个动态的GI的系统;第二个就是动态GI开起来以后,最大我们需要面对的问题还是一个性能问题,如果我们的性能不能满足,那么我们可能会降低它的质量,但降低质量后,它的RT的GI会有一些噪点产生,这也就是为什么我们暂时没有在项目当中直接使用RTGI的这么一个原因。
光线追踪优化
其实我个人也觉得也是比较关键的一块,就是关于光线追踪的优化。因为如果光追再好,但是不能把它优化到我们需要的一个性能标准,其实在真正的游戏项目里面也没有办法去推广的。
这个场景我们可以看到,在它的左侧有一面镜子,它的右边有一个纯金属的管子。现在我们在这个场景里切换一下它的视角,这块儿大家可以看到,我们是通过这个镜子来去看它对面的这个纯金属的管子。
这时候我们发现一个很奇怪的现象,就是管子变黑了。这是为什么呢?因为大家可以想象我们通过镜子里面看到这个管子,能看到管子首先这已经是光线反弹的一次,那么这也就是我们下方看到的,所敲的一个反弹次数的命令,当前帧数也是在将近于60帧的这么一个状态。
但是如果我们希望镜子里面的这个金属体它上面依然有反射效果,这就得二次了,这也就是为什么你现在看到的是一个黑管子,现在我们把它提到二次。
现在金属管子上面的一些细节,周围的一些物体,也就能被准确地呈现出来,但是很不幸的是,我们会发现它的帧数已经瞬间掉到了19帧左右。我们接着继续增大它的反弹的次数到三次。
这时候其实画面上跟之前没有太多的变化,但是帧数掉得依然比较可观,现在就是8帧都不到了。
所以通过这么一个简单的演示,我们可以得出来,在我们使用光线追踪反射的时候呢,随着它的Bounce的增加,它的性能掉得是非常非常的厉害。
那么这样就带来了一个问题,就是我们如何去平衡这个点?那这个时候我们可以在这个管子的附近加一个反射球,让它抓取这个管子周围的一些静态模型的信息,然后我们可以通过RT的命令,让这个渲染的时候,在第二次反弹的时候而采用这个反射球作为一个视觉上的弥补。
这样既平衡了我们性能上的不足,同时又带来视觉上的一个比较好的体现。所以通过这种方式,在很多比如说一些镜子,金属比较多的地方,我们可以用。
这个场景也是一个室内场景,我们知道RT其实对于反射这块,无论你反射的Roughness是多高多低,它都会进行一些反弹。
但我们可以仔细地稍微考虑一下,如果当我们遇到的这个物体的平面是比较粗糙的,那其实就算它上面有些反射,我们从视觉上带来的体验并不是很大,所以这个时候我们可以限制一下它做大的一个Roughness反射的一个值。
比如说像这张图上面,我们把它小于等于0.2的Roughness值才认为需要参与到RT的反射当中,但是这样又带来第二个问题,因为对于一个美术制作人员,就是他如何能比较精准地知道哪些地方的材质Roughness是小于等于0.2呢?是参与到RT的反射当中呢?
所以这个时候我们可以做一个后处理的材质,我们把所有小于等于0.2的Roughness的这个区域,把它以这种绿色形式标注出来。这样的话,对于美术同学来讲可以一眼就会知道,我哪些地方是参与了RT的反射的,我需不需要有这么多的地方进行反射。
同时这也作为我们一个性能上的控制,可以很好的预览。
现在大家看到这个图是一个材质的节点图。
因为我们整个项目当中采用了母材质的流程,也就是所有的美术人员他们自己并不需要制作母材质,只要通过TA做好的一个母材质就直接可以调参数,就可以用了。
这样的话,我们母材质的一些功能相对来讲是比较复杂的,比较多的,但是我们可以试想一下,有个场景有一面镜子,然后旁边有一个很华丽的物体,用了非常复杂的材质,比如说一个瓶子,这个瓶子用了很复杂的材质,从镜子里面去看这个瓶子。
首先,那么反射里面这个瓶子的像素肯定是比较小的;但是如果我们在反射里面看到所有的材质效果,依然是沿用了这么复杂的一个运算的话,其实在视觉上的体验并不能很大地让我们能明显地感觉出来。
那这样,我们就引入了一个RT Quality Switch的这么一个节点,也就是说,但我们从反射的物体里面去观察细节的时候,我们并没有让它完完全全以实打实的那个全节点的运算参与到RT反射当中,我们只给了一些相对来说比较简单的PBR的节点,比如像Normal Roughness Metallic这样的值,这样就会大大精简我们的一个运算效果。
这边有个例子,那么目前在这个镜子前面站着三个角色,其实我们仔细可以看一下,角色的衣服上面增加了一些细节,我们叠了一些细节纹理。此时此刻我们的RT的Quality Switch并关闭,是开着的,这些黄颜色的衣服上面它也是由这些细节的纹理呈现的。
但这个时候,我们会看看整个光线跟踪的反射所需要的的毫秒数是在4.46毫秒,但我们开启了RT Switch以后呢,我们把反射里面的黄色衣服上面的一些细节材质就直接干掉了,不需要再去渲染了。
所以说从画面上面来看,并没有太大的差异。但这个时候,它的RT反射所需要的毫秒数降低到了4.1毫秒,今天仅仅是一个小的例子,我们可以想象出如果所有的母材质都应用了这些RT Switch的一些优化节点,在很多大场景中会提供我们很多性能优化的一些节省的资源。
这块就是我们也用了一些蓝图,把蓝图上面经常需要用到的,有一些RT的参数预设到这个蓝图里面,然后我们可以把这个蓝图以Volume的形式放到两个不同场景的一个交界处。
比如像这样一个室内和室外的区域,因为室内和室外首先它的光照信息不太相同,同时它内部拜访的一些物体的结构、密度、材质也可能差别比较大,那么我们通过这样的一种形式就可以在不同的环境下切换两种不同的RT优化参数。
这样就会得到整个游戏性的一个平衡,同时也能满足一些视觉上的效果。
最后这块就是列举了项目当中常用的一些RT优化的方式、一些命令和一些参数,其实总体来说,就是优化这块你得牺牲一些可视化的细节,来换取一些性能上的提升,然后最后去寻求两者的一个平衡点。
我觉得无论是光线追踪还是我们传统的光栅化渲染,他们的目的其实都是为了将更好的画面带给我们,但是自从我们采用了光线追踪以后,这将为我们打开一个通往真实世界的大门。
以上就是我给大家带来的关于光线追踪的分享。
文/谢渊
来源:腾讯游戏学院
原文:https://mp.weixin.qq.com/s/IQk1q1FnaKwYZpOD0DEp7w
|
|