首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

TGDC|用技术诠释国风浪漫的归去来——《天涯明月刀》手游开发历程

2020年12月8日,由腾讯游戏学院举办的第四届腾讯游戏开发者大会(Tencent Game Developers Conference,简称TGDC)进入了第二天的议程。来自腾讯互动娱乐《天涯明月刀》手游的引擎技术负责人刘冰啸先生,分享了项目的开发历程,包括其中几项关键点的技术决策和开发经历。以下是分享视频和文字实录:

《天涯明月刀》手游是一款基于真实的物理渲染的国风MMORPG游戏,它延续了天刀端游的画面表现力,在手游平台性能受限的环境下,达到了这样的效果,甚至超越了端游的表现。这可不是简单的画面移植,背后的一切是怎么发生的呢?我作为天刀手游引擎技术负责人,将给大家带来背后的开发历程。

《天涯明月刀》手游的技术落地与优化

天刀手游的先进技术,是如何在天刀里面落地的。

我们去考虑手游引擎开发的几个重要要素:首先就是画质,我们需要进行选择,是希望它突出远景,还是希望它突出近景;画面的表现上是优先室内,还是优先室外;整个画面效果是鲜艳,还是更加自然;在光与影之间是做取舍,是优先光线的表达,还是优先影子的效果。

第二是帧率,天刀手游属于强交互的MMORPG动作类游戏,我们需要一个高且稳定的帧率。通常在PC开发的时候,我们只需要顾虑到两个方面,先达成画质的效果表现之后,通过优化来不断地提高游戏的帧率;我们帧率达成一个目标点之后,再想办法引入更好的画质的表现。两者产生不停的迭代,来提升整体画面的效果和优化表现。

而对于手游来说,我们引入了第三个维度,功耗。功耗对于手机而言,是非常重要的一个维度,它不仅会产生发热,而且还会影响到玩家游玩的单次时间。

在这三个维度上,其中任何一个维度的增加,都会影响到其他另外两个维度的表达。对于《天涯明月刀》手游的优化来说,我们引擎组采用了各种各样的技术栈,来去获取更好的优化效果。

比如对多线程渲染引擎进行了优化,把大部分可以并行的工作——例如动画、布料等,都放到其他的线程中去运行。使用了ISPC来自动生成NeonSIMD的代码,可以更好地提升CPU的执行效率。在GPU和CPU的遮挡剔除中,我们使用了不同粒度的遮挡剔除算法,首先启用了Vulkan API,然后通过Vulkan API 对GPU的内存进行更好的粒度上的管理。

我们并且实现了GPU Driven的渲染管线,用compute来去辅助优化各项其他的渲染技术。在画面上,我们使用了PBR的材质,以及真实的物理单位的lighting,获得更好的真实的HDR的画面效果。

在《天涯明月刀》手游的每次测试中,我们都会进入一个重要的优化或者功能。

第一次测试中,我们对Unity 进行了多线程的框架改动,把渲染线程和提交线程从主线程中剥离出来。因为在手游的开发环境里面,一个主要线程的持续工作会带来手机芯片的功率提升,而它的功率提升会带来更多的发热。

第二次测试中,引入了Vulkan API。Vulkan API相对于GLES来说,有着更好、更轻量级的调用。经过测试,我们可以做到在提交线程上获得30%的CPU效率提升。因为Vulkan API是更加自由的开发方式,可以在其中进行各种各样的优化。例如对一些比较重的Vulkan API操作,Descriptor Set的绑定,layout的绑定,都可以采用一些Cache的方式来去做。这么灵活的Vulkan API,使得我们在处理优化问题的时候可以获得更好的解法。

第三次测试中,我们引入了GPU Driven的技术。通过GPU Driven技术,我们可以把大部分CPU上的工作转移到GPU上去运行,不仅提升了GPU的效率,也减少了从GPU到CPU之间的各种传递的带宽。这项技术我们用在了地形上、植被上,在家园里面也运用了这些效果。

另外在第三次测试里面,我们修改并提升了整体的光照表达,引入了自动曝光,提升了Tonemapping效果,解决了由于真实物理单位引入之后,在不同的光照环境下lighting体现的一些细节颜色丢失问题。我们重新对sky lighting进行了定义,使得整个场景的室外表现更加丰富和具有对比度。

采用GPU Driven技术对渲染技术进行优化

这里我主要讲一下,在第三次测试中,我们采用GPU Driven技术来对渲染技术进行优化的一些要点

在开发过程中,我们经历了多线程的优化,发现了做手机平台的一个优化甜区——手机的多核,比我们在端游时代开发的时候更早的进入了多核时代,现在安卓手机很多都是四加四的多核架构。在这些架构下,都是以能让开发者更多的使用这些辅助的计算单元来提升计算效率。

对于我们引擎组来讲,我们采用了两个方向。第一个方向是尽可能的剥离主线程上的计算,通过dispatch到小核上、dispatch到其他线程上来提升它的计算效率。另外一个方向,把计算转入GPU compute。

为什么GPU compute是如此重要呢?它是现代渲染框架的一个基石。大家都了解,除了GPU进行光栅化处理部分之外,compute能够完成大量的GPU渲染流程上的操作,包括计算光照、计算材质等等。

另外,compute的渲染语言完全能和GPU本身的硬件对应起来。例如里面的Local Data Storage机制、ThreadGroup和Thread利用率的概念,都能很好地在compute语言上表现出来。

第二点是使用compute能够发挥Vulkan更大的潜力。因为我们可以使用Vulkan对GPU的同步行为做出非常好的控制,而compute作为一个独立的单元,可以把compute计算很好的和GPU的其他计算并行起来。例如compute可以和一个带宽优先的,例如shadow pass进行并行。

另外,compute可以是一个单独的Queue,对比vs和ps的整套pipeline来说,它是一个非常简化的单元,非常容易到处去摆放。

第三点,采用compute的话,我们可以给GPU Driven和Bindless打开更广阔的优化空间,甚至可以使用Async compute方式来更进一步的并行compute和GPU的单元。

对于《天涯明月刀手游》来说,打开compute的关键突破点,就是GPU Driven的地形系统。 看一下使用compute作为GPU Driven的一些研究方向。

在最近的几年里,GPU Driven是一个相对来说比较热门的研发方向,育碧有一篇文章,GPU Driven的渲染管线,主要是讲《刺客信条:大革命》的开发。EA的寒霜引擎也提到了GPU Driven的pipeline。2018年《FarCry5》也实现了相关的功能,2019年育碧的《Trials Fusion》也完成了相关的一些实现。但是在手机的这个领域上,目前还没有一些技术能够真正在在线的产品中体现出来。

什么是GPU驱动的渲染管线?首先是GPU掌握实际的渲染控制,可以提供更细致的渲染力度。例如做渲染剔除的时候,CPU只能控制在object level,使用Object Bounding来去做剔除,在GPU这个层面上可以做到更进一步的控制。我们可以在Mesh cluster级别上通过切分Mesh来获得更好的粒度控制。

它的另外一个好处是,不需要GPU和CPU之间的数据来回传递,在理想情况下,GPU Driven甚至都可以使用一个Drawcall来绘制完整个场景。当然这个需要compute shader的支持,以及indirect drawing相关API的提供。在Vulkan 1.0的情况下,我们是都可以拿到相关的支持。

GPU Driven流程

看一下在《天涯明月刀》手游里面,GPU地形实施之后的结果。

首先介绍一下CPU地形常用的算法。我们上一个版本里面的地形算法是CPU端的Geometry Clipmap算法,它采用的裁剪剔除方式是视锥的裁剪剔除方式,对比GPU Driven来说,少了depth剔除,计算LOD的方式是根据距离计算的。而GPU Driven是可以根据距离和地形块的复杂度来计算。

送入clipmap方式的顶点处,因为有两个pass,其中一个要通过Virtual Texture来使用,这样的话它需要21万*2的顶点数。而在GPU Driven情况下,因为获得比较好的剔除效果,它只需要8万6的顶点数。

看一下最后的GPU时间。在GPU Driven的情况下,我们在iPhone8P上可以获得2.9毫秒的时间开销,这比通过CPU的方式下节省了近四分之一的成本。而在CPU端获得收益更大,我们提交线程和渲染线程,每个都可以获得五毫秒以上的收益。

GPU Driven的流程来讲的话,主要包含GPU Driven和Virtual Texture两个算法。这两个算法的实现有互相的交叉,出于简化,我们只讲一下GPU Driven相关流程上的一些算法。

首先是一个深度的mips生成,这部分算法是在compute里面实现的。其次在compute里面实现GPU遮挡剔除,使用上一步制作的深度缓冲buffer。GPU的遮挡剔除主要是通过计算你送入的Patches的尺寸,看它处在哪一级的深度缓冲上,来对比这一级深度缓冲的深度和你的深度,来决定是否被depth剔除掉。

通过这个流程我们可以获得可视的的Patches数目,根据可视的Patches数目,我们仍然通过compute用来做indirect的Arguments buffer来生成出来。

第四步就是把这些准备好的indirect buffer给绘制出去,这个就是整个地形GPU Driven的渲染流程。在这里面有一个非常重要的优化,也是在compute上可以达成的,这个优化主要是利用compute里面的Thread shared memory的方式来做。这种优化减少了CPU到GPU之间的多次的dispatch,减少了binding memory pingpong操作。

在AMD的一篇文章里,也会有更好的效果,它能应用的范围主要在于对你的buffer做filter。例如我们经常在渲染管线中提到的Bloom/高斯模糊、自动曝光,这些对于区域进行filter的操作都可以采用这些方法来获得优化。

GPU Driven怎么应用在《天涯明月刀手游》的地形体系里?

首先我们会做第一次的dispatch,这个dispatch会产生16*16的线程组,每组128条线程,把这128条线程读入深度放入mips里。第二步通过同步的方式,把上一级的mips相邻的四个点取出来,合并选择最深的单位,写到第二级的mips里,依次类推,完成四级的写出。在第二个dispatch里,我们用同样的方法,dispatch一个线程组,128条线程,把后面 32*16的mips写入完成。

在GPU Driven的地形系统里面,我们仍然有另外一个机制的优化,这个机制是LOD体系。在《FarCry》的实现里面,它主要采用的是CPU 四叉树的方式来组织LOD的patches,根据距离更新四叉树上的节点,在选择CPU 四叉树上节点,根据相机的距离选择这些节点,送入GPU进行indirect buffer和GPU Culling。

而对于《天涯明月刀》手游来说,我们把这些patch信息先通过offline的方式bake出来,在每一个相机发生位置的时候,去更新这些bake出来的信息,根据这些信息去对改变的patch做过滤,生成新的indirect buffer。

看一下差异,首先第一步,我们读到了所有的patches属性,第二步我们根据视锥裁剪,获得视锥裁剪之后,第三步我们再用HiZ产生一次depth裁剪。完成这三步之后,就可以生成Indirect Draw Arguments的buffer来去dispatch出去。

在我们完成GPU Driven地形之后,我们引擎组会把这些所有的流程重新梳理一遍,再根据梳理出来的流程,去选择可以应用GPU Driven的其他渲染模块。其中比较重要的一个渲染模块就是场景的植被管理。

有经验的渲染程序可能直接会意识到这一点,其实草的geometry和地形的patch、或者是sector的管理是一个非常类似的概念,它们都有LOD,有不同的geometry的表现。我们绘制这些geometry LOD的时候,最好的方法是通过Multi Draw Instanced Indirect的方案来去做。

另外一点是这些草的Texture。每种类型草的Texture,对应在地形上,更像是一个地形的Virtual Texture机制,我们也可以用bindless的方式去做绑定。可是在Vulkan 1.0的平台情况下,我们这两个API,Multi Draw Instanced Indirect或者是bindless,都没办法获得更好的支持。所以在《天涯明月刀》手游的实现里,我们只能采用将草的每种类型完成一次GPU Driven的culling和Draw Indirect Buffer的生成。

另外在《天涯明月刀手游》里面,比较适合GPU Driven的场景是家园的渲染。家园的玩法在游戏里,主要可以让玩家尽可能多的定制。我们整个家园的地形、地表、墙壁、物件、地板等等,它是需求非常多的geometry类型。第二,它的区域相对来说比较小,只有128米*128米的自定义空间,而在这种小的自定义空间情况下,遮挡剔除是必须要做的非常好的一种技术。

从这两点看起来,GPU Driven非常适合应用在家园渲染的情况。问题就在于,家园里面的这些物件其实和草的类型一样,都非常依赖于Multi DrawInstanced Indirect和bindless这两种API的实现。对于《天涯明月刀》手游来说,我们只能退而求其次,利用地形步骤算出来的HiZ的buffer做遮挡剔除。

我们送入一套做遮挡剔除buffer的内容,通过CPU从readback的方案来获得这些buffer的遮挡剔除的结果,在CPU端组织尽量多的instance对象。即便采用这种方式,在家园渲染情况下也获得了比较好的渲染效率。

在完成了compute的基础机制上,我们在其上面也做了各种其他的尝试,其中有一条是,完成了在ASTC和PVRTC的GPU实时压缩。这个也是通过GPU compute来实现的,这个功能可以用在角色的妆容系统上。

对《天涯明月刀》手游来说,整个角色的妆容系统需要完成多个Feature的绘制,如果不能很好的去做,baking到一张贴图上,在实时渲染的情况下它会产生更多的开销。实时baking到贴图上,我们还希望它能够尽量去做压缩来减少内存的使用。我们测试了一些compute compression的效果,在PSNR和效率上都能获得比较好的表现。

另外,我们尝试对于VirtualTexture的压缩。前面提到地形的VirtualTexture技术需要更新大量的地形块数据,越多的地形块数据才能使你更新的频率变低。在《天涯明月刀》手游里,我们使用2048*2048三张不经压缩的材质,通过调整贴图的尺寸和贴图数量以及压缩方式,基本上能把大张贴图的时间控制在四毫秒以下。

这个时间其实已经可以达到使用的效果,从远处来看,其实地表材质很难观察到差异性。下图是压缩前后的两张对比。但是这种算法,对比一下它的细节表现,我们仍然可以看到,在开启压缩的情况下,它其实是有一些Blocking的瑕疵,这样的瑕疵在游戏的画面品质情况下是不能接受的,所以这种方案只能把它放弃掉。

另外一个compute应用的领域,我们尝试了Cluster deferred。Cluster deferred主要应用于家园室内场景,首先它是一个封闭的空间,它仍然和之前家园的环境一样有大量的动态的物体、动态的光照效果。

在下图可以看到,家园物体里面有大概55盏灯,从左上角的一张蓝色的背景图来看,这个区域最多是8盏灯以上的照亮。在这个技术方案实现之后,我们发现它仍然解决不了几个问题。

第一个问题就是,deferred本身的固有问题,它的带宽问题,设计更好的GBuffer格式也需要去应用一些API,例如subpass,或者一些更好的pass combine的操作。第二是在于材质的复杂度,一个deferred的材质和forward材质,在整个大世界的使用是很难做兼容融合的,这也增大了shader的复杂度或者是工作量。

这种方向也许对未来是一个比较好的技术点,但是在现有的架构下,我认为还是不够成熟,这套方案仍然是放弃掉的。

《天涯明月刀》手游compute技术落地

来看我们应用compute技术落地的情况。

我认为有几个决策是非常重要的。第一,需要有一个非常好的基础,这个基础在于从多线程开始,我们意识到整个计算体系应该不同的去往compute方向,或者是往其他线程方向去使用,通过减少主线程的开销成本,去提升整个游戏的性能效率。

我们让有比较好技术功底的同事完成了GPU Driven地形的突破,在这个基础之上,继续使用GPU Driven的技术解决了一些场景植被的问题,以及家园的渲染效率问题。我们compute还解决了自动曝光等等相关的问题。

第三点,在于技术落地和产品需求之间的要求。有很多产品技术可能比较先进,但是它缺乏能达到产品需求的质量品质,我们对于这种技术也只能忍痛割爱。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201215A0FP0Y00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券