体素与阴影贴图:为Roblox选择新的照明系统





在过去的四年中,体素已成为Roblox世界中的照明系统。但是迟早,一切都需要变革的时刻。这就是为什么开发人员想知道下一步该怎么做的原因。



照明很棘手,因此在选择新技术时要注意许多因素。为了促进决策制定,Roblox已建立了两个未来系统的原型:所谓的体素和阴影贴图。为了了解两者的局限性,首先要了解它们的工作原理。

注意:文章中屏幕截图经过整理,因此体素始终显示在左侧,阴影贴图始终显示在右侧。





实施:体素



尽管此特定系统在游戏中已经使用了很长时间,但此处考虑的选项已进行了许多改进。



世界数据将转换为一组体素网格:每个网格都围绕角色居中,并且体素大小可以从1到16(总共5个网格)。每个体素包含0到100%的占用信息。然后根据此填充度和光源/方向信息为每个网格中的每个体素计算照明数据。上面所有这些都是在GPU上发生的,因为中央GPU的速度不足以更新如此高的密度的众多体素。



系统将所有数据存储在体素中,尤其是-对于每个可用的体素,都有以下数据:



  • 饱满度(多个值描述每个体素的饱满度);
  • 天光(从体素可见多少天空);
  • 太阳阴影(有多少太阳被体素遮挡了);
  • 灯光对象/圆锥体的颜色(局部光源在体素上的颜色/圆锥体的近似值)。


此信息随后用于计算给定分辨率下每个像素的颜色。屏幕和体素分辨率可以相互独立地进行调整。体素网格的各个部分可以随着灯光/对象的移动而逐帧更新。



实施:阴影贴图



图片



此方法使用栅格化来计算大多数阴影效果。它分三个阶段执行。首先,对于每个阴影等级,我们更新阴影贴图,从光源到场景中发射多束光线的等级,并记住相交结果。然后,我们构建一个空间加速结构,在该结构中嵌入每个可见光对象,该对象本质上是一个截头圆锥体素网格(也称为froxel网格)。



网格覆盖了相机所看到的整个游戏世界。在每个froxel中,我们写下与之相交的所有轻物体的列表。最后,为了计算所有光源的影响力,在渲染场景时,我们为每个像素寻找包含该像素的froxel,遍历所有光源,对于每种光源,我们使用第一阶段构建的阴影贴图分别计算其影响力。



系统以两种结构存储数据:



  • 阴影图集(将所有可见光阴影图打包成一个大纹理);
  • 轻网格(froxel网格,可将相机中的一个点有效地转换为灯光列表)。


每个像素的颜色是动态计算的,不会显式存储。阴影图集的某些部分可以随着灯光/对象的移动而逐帧更新。



性能:体素



体素技术更适合于缩放:降低输出的质量,我们可以减少体素网格的数量或每帧更新较少的体素(这导致“光延迟”,即与更新对象本身相比,对象曝光的更新速度较慢)。



体素具有三个复杂度特征:几何复杂度,光复杂度和像素数。几何复杂度仅影响体素化的成本,因此添加更多对象不会引入延迟。光的复杂度仅影响计算它的成本,而与几何复杂度或像素数无关。最后,最终的像素颜色是根据体素/灯光/对象的数量计算出来的,因此我们可以在不影响照明质量的情况下缩放分辨率。



体素性能的计算方式为O(G)+ O(L)+ O(P),其中G是三角形的数量(几何复杂度),L是光的数量,P是像素的数量。



不幸的是,由于N3和GPU对于所需的刷新方法而言并不理想,并且无法维持良好的性能管理,因此体素的数量级会增加,因此体素的峰值性能并不是最佳的。通过对GPU计算的足够研究,可以弥补性能损失,但目前基础成本可能仍然很高。



性能:影子卡



影子卡基于栅格化设计,因此对GPU更为友好。可以通过缓存/延迟更新来部分减少更新阴影图集的成本(从逻辑上讲,这会导致额外的延迟)。优化几何图形表示(包括网格的细节层次)还可以降低方法成本。



但是,更新复杂场景中的阴影仍然很昂贵,因为该成本取决于几何细节量和投射阴影的灯光数量。在建筑物内部,对于移动的灯光,需要每帧重新渲染整个建筑物,以更新该灯光的阴影信息。建筑物中大量移动的灯光投射会导致性能降低:我们无法立即更新框架中的所有灯光,从而导致视觉伪影。



此外,此方法不允许将分辨率参数与光量分开:对于每个像素,我们必须重新计算覆盖它的所有光源的影响。此步骤也无法缓存,这会导致在高光照场景中以高分辨率显示性能问题:4K房间中20个重叠的灯光可能需要1.6亿个灯光估计。



阴影贴图的性能计算为O(GL)+ O(LP),其中G是三角形的数量(几何复杂度),L是光的数量,P是像素的数量。



绩效:评估



为了更加清晰,两种方法都用于特殊选择的游戏级别。请注意,这些是预先存在的级别实现,不是专门为性能评估而设计的。





巴黎(太阳阴影,很少有非阴影光源)







  • 体素:阴影更新-6毫秒,场景渲染-1.5毫秒;
  • 阴影贴图:阴影更新-1毫秒,场景渲染-2.4毫秒;
  • 计算基本体素阴影的成本较高,因为GPU难以处理。


洞穴(许多产生阴影的来源)







  • 体素:阴影更新-7毫秒,场景渲染-0.9毫秒;
  • 阴影贴图:阴影更新-10毫秒,场景渲染-2.1毫秒;
  • 由于大量的几何图形和移动的灯光,更新阴影贴图非常昂贵。


西方(许多阴影投射源)







  • 体素:阴影更新-8毫秒,场景渲染-1毫秒;
  • 阴影贴图:阴影更新-15毫秒,场景渲染-2.5毫秒;
  • 在移动灯光和大量三角形的情况下,更新阴影贴图的成本很高。


1000个无阴影的光源







  • 体素:灯光刷新-20毫秒,场景渲染-0.5毫秒;
  • 阴影贴图:光照更新-0.5毫秒,场景渲染-5毫秒;
  • 在这种情况下,光和体素的重叠量会减慢体素的更新速度。此外,您可以看到在近段时间未执行“每个体素中的一个光源”的近似计算。


业绩:结论



阴影贴图可以很好地适应工作负载,但是需要考虑两点:



  • 每像素的成本随着分辨率的提高而增加,这使其仅在中等分辨率(1080p)时才是可行的解决方案。超过1080p要求一个非常好的GPU。
  • 在许多动态灯光的几何形状复杂的情况下,渲染阴影的成本增长非常迅速。可以通过更好的剔除来弥补这一点,但是在现阶段,这仍然是一个基本问题。


同时,相比之下,体素性能对水平含量的依赖程度要小得多,但基础成本却高得多。这可以通过改进GPU算法和减少体素来弥补。



内存需求



阴影贴图和体素的内存要求取决于所需的质量。



对于体素,每个阶段将几个纹理存储在内存中,因此它们的总大小取决于阶段数和每个阶段的大小。当前,共有4个阶段(体素大小为1..8),每个阶段128x64x128体素,总共使用了128 MB的VRAM。可以再启动2个级联(0.5个体素和16个体素)或重新配置现有的级联,这将使该值增加到192 MB。相反,您可以在内存有限的系统中减少级联的数量(删除一些紧密的级联),然后两个级联(4..8)的最小内存影响约为64 MB,三个级联(4 ...)约为96 MB。十六)。



在阴影贴图的情况下,使用阴影贴图地图集和froxel网格。后者部分取决于分辨率。如果需要降低阴影的质量以提高性能/内存,则可以减小阴影图集的大小。当前系统使用73 MB的视频内存,其中大部分(64 MB)被阴影图集占用。您可以减少它,从而限制暗光的数量或阴影的质量。您也可以考虑阴影映射的一些选项,这些选项需要更多的内存来支持半透明性,这意味着它们将占用更多的空间(最多130 MB或更多)。通过减少阴影图集的大小并使用其更简单的版本(大约需要25 MB),可以达到对系统内存的最小影响。



相比之下,当前的照明系统具有两种模式:高(PC)和低质量(移动)。PC版大约需要40 MB(24 MB RAM,16 MB VRAM);移动-大约11 MB(6 MB RAM,5 MB VRAM)。



实际上,就内存影响而言,这两种方法都相当接近,但是对于相同的光照/阴影范围,阴影贴图的可伸缩性更高。



移动兼容性



游戏的观众越来越多地移动,这意味着在比较选定的实现时,还必须考虑不同的游戏设备。在高端设备上,应该有足够的API功能来实现这两种方法,但是当然,就内存和性能而言,它们可能不那么实用。



现有的体素照明系统非常适合移动设备:它支持许多复杂的照明功能(阴影,天窗等),并在CPU上执行大多数复杂的计算,因此对GPU性能和功能集的要求最低。 ... 由于在可预见的将来仍需要低成本移动设备和PC支持此系统,因此出现了支持大量设备的几种选择:



  • 以移动形式保存现有系统,新的将仅是PC /控制台 这意味着很大一部分用户将无法访问新系统。
  • ( , ), /.
  • low-end , , .


在所有这些情况下,有必要回答有关内容兼容性的问题,因为该平台的主要承诺之一是“一次下载内容并在任何地方运行”。我们仍然需要努力。乍一看,新的体素解决方案从提供旧系统到新系统的质量/行为的一致性方面来说似乎更好,而阴影贴图代表了向另一种质量更突然的过渡,但是与此同时,它们更可能与移动设备上有限的功能相处。



质量:光源



阴影贴图解决方案在模拟光源方面提供了可靠的信息:在下面的屏幕快照中,在阴影贴图的情况下有1000灯,您可以看到完美再现的镜面高光-使用BRDF建模,可以为我们提供所需的光反射。



体素解决方案从根本上说更糟,因为它近似于光对每个体素的影响,就好像它仅来自一个光源一样。







由此可见,镜面反射的质量会下降:因此,在具有镜面反射高光的区域中的体素的情况下,颜色以1:1的比例合并,从而产生黄光而不是绿色和红色,尽管场景中甚至没有黄光。相反,阴影贴图解决方案可以准确地模拟颜色组合。



在某些情况下,尽管将来可以改进这些结果,但我们根本无法获得令人信服的结果:







在上面的示例中,您可以看到弯曲的,细长的,扭曲的镜面高光,并且在其中一个对象下方的多个体素中根本没有光信息。阴影贴图的相同屏幕截图给出了更好的结果。



质量:阴影



阴影贴图的定义质量是准确性,而体素阴影是柔和的。阴影贴图提供了相当清晰的阴影,并具有尽可能少的细节,但又足够精确以为角色创建令人信服的阴影。另一方面,体素算法对于创建真正的柔和阴影非常有用,但是,小细节的阴影要么根本没有记录,要么形状不规则。







由于这个原因,阴影贴图的一种变体目前用于渲染角色阴影-但是,它更像是拐杖,仅应用于投射角色阴影的太阳。不考虑其他光源。



同样,加速体素的关键技术是使用级联。但是,这意味着充满度数据会变得更粗糙,因为距场景中点的距离对我们很重要。在这种情况下,阴影的质量也会随着阴影的源与接收器之间的距离的增加而降低:







在上面的屏幕截图中,最小体素的大小足以从桥上渲染出高质量的阴影,但是桥离水面太远,因此即使在只要水面附近的体素非常好。



质量:天窗



体素管道中的一个重要功能是天光系数的计算,该系数决定了从当前体素可见多少天空。它用于混合室内和室外照明,并且在提高其质量方面非常有效。在下面的屏幕截图中,即使在阴影区域,房屋的外部也应比内部明亮。体素解决方案可以很好地计算和再现此系数,但是在阴影贴图的情况下,它不存在,因此不可能使阴影变暗。







质量:几何精度



值得注意的是,体素和阴影贴图之间的几何表示形式存在根本差异。



体素假定光引擎支持的所有对象都可以“体素化”-也就是说,对于游戏世界中的每个体素,都有一种快速的方法来计算对象与体素之间的相交量。这对于原始形状效果很好,但是诸如CSG和MeshParts之类的复杂对象已经是一个严重的问题。现在,粗略的分解和其他一些有效的像素化技巧可以帮助解决此问题,但这通常会导致可见的伪像。同时,阴影贴图使用渲染中涉及的相同多边形表示,因此它们可以完美地表示所有对象的形状:







质量:漏光



尽管阴影的形状极为重要,但必须正确处理不可见像素,这一点甚至更为重要。当不同的近似值违反此规则时,就会发生所谓的漏光-可见条纹,这会在高对比度环境中(例如,在室外阳光直射的建筑物内)引起最多的问题。这是一个漏光的例子:







这是地板上紧靠墙壁的薄薄的照明部分。阴影贴图可以更好地保留光遮挡并解决此问题。



体素有多种泄漏源。其中一些可以在保持各向异性填充度的同时得到缓解:例如,现在内存为每个体素存储3个值,指示所有三个轴的``体素沿轴的投影量是多少''。不幸的是,尽管这有助于微妙的细节投射阴影而不管阴影的厚度如何,但它无法修复所有泄漏。在这方面保证完全阻挡光线的唯一方法是使零件的厚度是体素的两倍。此外,泄漏随体素的大小而增长,这意味着在较低的质量级别和/或较大的距离处,泄漏变得更加明显。



阴影贴图并没有完全密封,但其中的泄漏问题就少得多-例如,在此实现中,厚度为0.4体素的对象将不会透射可见光(厚度为0.2体素的对象可以透射一部分光,但也可以从中透射出一部分光。您可以在将来摆脱它)。



质量:结论



影子卡在质量的大多数方面都很出色。唯一重要的沉降区域是天窗比的计算。这可能需要使用将体素化用于天窗的混合方法,这会给体素管道带来一些障碍-或可能有针对此问题的替代解决方案。能够支持柔和阴影也很不错,这可以通过对阴影贴图算法进行一些扩展来实现。



体素提供可接受的质量,但是与阴影贴图相比,它们损失很多,尤其是在阴影和镜面高光的保真度方面。我们必须以某种方式解决这些问题,以便能够实现能够为玩家提供美丽阴影的体素照明,因为使用当前解决方案只能提供来自太阳的阴影,这似乎与游戏世界的未来愿景不兼容。



可见度:半透明



对于不透明的对象,渲染阴影是一个经过充分研究的问题,但半透明完全是另一回事。由于在体素系统的情况下,光基于填充度值穿过体素单元,因此维护粒子效果和场景中其他半透明对象产生的低频(柔和)阴影(包括自遮蔽)产生的半透明阴影并不太容易维持半透明阴影粒子会自己效果:







以下是此效果的视频:





当前不支持阴影贴图的半透明性。这意味着如果我们要支持粒子或其他投射阴影的透明对象,则必须找到另一种解决方案。对于替代阴影贴图表示,已经进行了一些研究,可以满足该用例的需求,但效果如何尚待观察。



可见性:植被



尽管阴影贴图在半透明性方面不能做得特别好,但是无论它们是用几何图形还是纹理建模,它们都可以显示物体(例如植被)的精细细节。体素不够小,无法满足此用例。另外,在这种情况下,要访问有关纹理的信息并不容易,因为它需要对网格表面而非体积进行精确建模。怀疑是否有可能使用体素从植被中获得美丽的阴影,而阴影贴图是否可以甚至通过现有内容也可以提供此阴影,如以下屏幕快照所示:







可见性:自发光



由于体素的实现方式,在不影响管道其他部分性能的情况下,将任意形状和数量的光注入到网格中相对容易。但是,在向阴影贴图添加许多灯光时,创建具有不规则形状的灯光会带来一些体系结构和性能问题。特别是,使用体素实现真正的自发光要容易得多:霓虹灯材料目前用于“发光”,但实际上并不向附近的其他物体发光。







是的,您可以添加其他灯光,但是自动完成所有操作会很好。阴影贴图对此不是很有用,但是体素根据需要支持任何形状的体素化,因此支持自发光对象的发光非常简单。



可见性:全局照明



全局照明(GI)指的是辅助照明效果的计算,例如来自灯的光线从墙壁弹起两次以在直射光线无法到达的区域提供附加照明。



Roblox中的GI非常复杂,关于它的大多数决策都必须牺牲动态照明,动态几何形状,性能和大型场景中的某些内容。这些牺牲都是不允许的。



鉴于内容的严格限制,尚不清楚哪种GI解决方案将是可行的。到目前为止,基于体素的GI似乎比其他方法更有希望。



当然,拥有基于体素的GI并不意味着使用它们来计算直接照明:当今大多数基于体素的GI研究涉及使用阴影贴图来计算直接光并改善体素的结果。



概要



基于以上分析,我们将汇总两种解决方案有效性的汇总表。斜体表示建议进一步的研究可以改善这一领域。表中的评分如下:糟糕<差<正常<良好<优异。







因此,体素非常适合于对间接照明进行建模,但是对于直接照明而言却不是那么好。同时,它们非常耗费资源,与维护大量设备的任务相关性很差。



这导致决定创建用于直接照明的阴影贴图系统。尚未明确找到解决天窗和全球照明问题的方法,但是很可能,这将成为两种系统的混合体。



All Articles