真·千人同屏 | 虚拟世界的“烟火气”

原创 1 收藏 评论
举报 2023-09-28

1695887643239735.png

我们为什么需要“千人同屏”?

在过去的两年,各式各样的虚拟场景层出不穷,有展厅展馆、有展览与音乐会、有景区古迹,但这些场景中多是寥寥数人穿梭其中,附加站在某个角落的NPC,这样的体验难免给人一种“单机游戏“的既视感,而千人同屏技术能够打破这样的既视感,是带给拟世界临场感的基石功能之一!

点击体验千人同屏

  灵境大陆-体验入口  

  音乐空间-体验入口  


  • 观看虚拟演出和表演,能感受到不亚于现场音乐节的热闹氛围


(光遇欧若拉演唱会)

  • 体验虚拟文旅活动,能和更多五湖四海的伙伴同游畅聊,更有现实生活


(逆水寒汴京夜市)

  • 对于线上峰会、发布会等会议,上千人会议现场更有“人声鼎沸”的热议氛围

  • 对于线上社区,大家真实的互动、分享、交流将会直观视觉化激发用户的参与感


千人同屏并不是全新的技术,在数年前的游戏中早以可以实现。但是在虚拟空间中,没有游戏化的APP载体来充分释放性能,没有用户有等待游戏加载的耐心,并且在以网页为主要实现方式的当下,虚拟空间需要快速载入、兼顾性能,并可实现多端兼容(如不同app/浏览器/AR/VR设备...),宛如带着镣铐起舞。

虽有如此难度,但“千人同屏”仍被轻易地附加在各类概念化的描述之后,看似可以被轻易地实现。这里想分享下我们理解的“千人同屏”:


用户可以在各式各样的虚拟人中无限制地选择自己喜欢的风格。在千人同屏技术的加持下,整个虚拟世界不再是整齐划一的虚拟人,而是像聚集了来自五湖四海的各色人儿,呈现千人千面、缤纷多彩的世界。


在真正的千人同屏里,用户不再是被限制在某个位置坐下或者站立(这样的千人万人同屏毫无意义),而是可以自由地操作自己的虚拟形象,去四处漫游,能做各种社交互动,在热闹的场域下充分感受真实的人与人之间交流,人与物之间的交流。


在虚拟空间中,能够认识新朋友遇见老朋友,是在虚拟世界中难忘的经历,如果此时看上去人山人海,实则只是程序操控下的机器人,那大可不必来到这里享受一个人的狂欢。

当然还有一些文字游戏类的说法,比如“实现千人同屏观看”这样以直接偷换了概念的方式硬套千人同屏技术,无不在说明千人同屏技术真实的难度。


为什么要实现“真·千人同屏”这么难?

在网页端要实现同屏千人流畅的实时互动,不同的环节都有巨大的挑战:后端对于用户位置信息的汇总处理、前后端通信细节的优化、前端渲染开销的控制......

下面具体分享下我们技术处理方案:


说到渲染性能优化,降低 draw call 数往往是最先想到也最有效的做法。一个 draw call 是 CPU 向 GPU 发送一次绘制命令,会产生两者间的一次通信开销。一旦 draw call 过多,这些开销积累起来,便导致渲染性能下降。

在以往的项目架构中,每一个角色人物作为一个单独的模型,渲染时都需要一次新的draw call,因此当在线人数大量增加时,draw call 数也就线性地猛增。

而实际上角色人物的种类并不多,只有三四个,因此大部分人物都是同一个模型的重复渲染,如果可以把这些重复的模型同时渲染出来,自然就可以大大降低 draw call 了。

说到重复模型,对于3d渲染有所了解的话,自然就会想到实例化绘制(Instanced Drawing), 它可以将同样的模型以实例化的方式合并成一次 draw call 绘制出来,是 WebGL 提供的能力,正好可以解决我们的问题。

可事情并没有这么简单。

我们的角色人物由用户操控,会跑、会跳,实际上是带动作骨骼的蒙皮模型(Skinned Mesh),这些动作互相独立并受人物当前活动状态控制。而 现有的移动端3d框架并不支持简单地将骨骼模型实例化,如果只是简单将模型实例化的话,只会得到一堆无法表现任何动作的僵硬实例,在世界中平移。显然这是不可接受的。


(未支持骨骼蒙皮模型时实例化绘制时效果)


(正常带动作的人物模型)

为了解决这个问题,可以有多种处理方案,其中一种是将动画烘焙到贴图材质中,这样就可以在 GPU 代码中通过采样的方式获取数据完成顶点变形;另一种是将动画的插值计算放在 CPU 上,再实时将当前动画数据上传至 GPU 处理

前一种的优点是性能更优,缺点是需要对动画的特殊处理,而且 GPU 编程限制更多也更复杂;后一种方案的优点是与现有的逻辑相适配,代码上也更简单,缺点是如果想要更加优化性能,还需要做其他特殊处理。

这里,分享下比较简单的处理方案。

如果我们搞清楚现有框架是如何实现非蒙皮模型的实例化、以及单个蒙皮模型的动作变形,再将这两种能力组合起来,就可以实现带动作蒙皮模型的实例化啦!

具体来说,骨骼蒙皮模型能够渲染出动作动画,在于它的各个顶点已经绑定了对应哪几块骨头以及它们影响的权重。当骨骼的位置、旋转、大小变化时(即动作进行中),渲染时便会逐帧去取到最新骨头的变换矩阵并将其作用到顶点位置上以改变原有的位置,这样就形成了动画。


(控制模型动作的骨骼)

要把这些逻辑放入实例化绘制,也需要有同样的操作。

顶点与骨骼的绑定与单个模型是一致的,只需要复用即可,而不同的地方主要在于之前只需要从一套骨骼中去取骨头的变换矩阵,而实例化后需要从多套骨骼中去取当前实例对应骨骼的变换矩阵。

因为每一个人物都有自己的动作状态,因此可以为每一个实例绑定一套骨骼,并由动画控制器去实现动作的计算。之后再将这些动作数据以数据贴图(data texture)的形式上传至 GPU,然后在顶点着色器(vertex shader)代码中读取使用即可。

当一个模型有20多块骨头时,每块骨头需要4个像素来存储共16个数字的变换矩阵,一套骨骼可用16*16像素的贴图且还有大量空余。即使实例数量(人物)增加至1000个,贴图大小也不会超过512*512像素! 

在新的着色器代码中,我们取到新的骨骼变换矩阵便可完成顶点变形。

完成实例化后,当场景中有1000个角色时,draw call 数也不会像之前增加到1000,而仍然仅为1,在渲染层面大大优化了性能! 


虽然 draw call 已大为降低,但1000个模型仍是1000个模型,顶点数和模型面数都不会减少,仍然是 GPU 计算中不小的开销。

为了减小大量模型渲染时的面数,我们采用了多细节层级(LOD)的技术方案。

简单来说,依据人物模型与相机的距离,可以选择不同细节层级的模型。这是因为当角色距离较远,其精细细节本就难以分辨,这个时候再去渲染一个精细的模型也没有必要,完全可以用一个更加粗糙的类似模型来替代。

粗糙的模型顶点数可以减少很多,而且还可以有多个层级,以更细化区分距离,达到既减少面数、又在视觉上无差别的效果。

另外,还可以在角色距离相机一定距离外完全隐藏模型,进一步优化性能。

因为不同的层级对应不同的模型,所以在实例化绘制时,也需要引入新的实例化模型,同时增加几个 draw call。但这几个 draw call 仍然远小于此前每个实例单独一个 draw call 时的数量,收益远大于开销!


虽然用实例化绘制在渲染能力上支持了1000人同时在线,但实际上在场景中不会始终都有1000人,而且因为采用了多细节层级,每个角色也可能会在几个实例模型间切换,因此就会有实例显隐的问题 。

因为底层实现的限制,想要控制实例化模型中实例的显示还是隐藏,无法简单在各个实例上去控制,而理论上最理想的方式是动态修改 count 属性值。此时,实例模型中只有前面 count 这么多个实例会进入渲染流程,而后面的实例因为不参与渲染,因此性能上最优。

我们在一开始也是这样处理的,将场景角色的变换矩阵设定给前n个实例,然后设定好实例化模型的数量,之后当角色退出场景或切换到其他模型时,只需要更新一个实例对应的索引序号、并减小 count 的值即可。这样就可以保持始终只渲染实际有效的模型


(理想情况下实例显隐控制)

但是在实际操作中,在切换数量及同步序号的瞬间,场景中对应的实例偶尔会有闪动。虽然不是非常显眼,但仍然是不稳定的体验。

为了解决这个问题,我们在探讨权衡后决定不再瞬移实例,而是在 GPU 着色器代码中处理实例显隐,并控制计算复杂度

虽然这样性能会略微劣化一点,但好处是用户体验更为稳定可靠。经过测试,这样虽然面数相对没有减少,性能却有不小的提高。


在处理实例化骨骼蒙皮模型时,我们还遇到了其他一些问题,其中包括模型影子

因为框架只支持非实例化绘制的骨骼模型,在我们实现的实例化模型上,默认的获取深度映射以渲染影子的着色器代码无法直接处理。

因此需要再增加自定义的深度材质并绑定自定义的着色器。此时将骨骼动画的作用应用在顶点位置变形上,即可以得到正确的深度映射啦!


(左:影子没有跟随动作变化;右:影子实时跟随动作变化)

此外,为了阴影性能的考虑,我们也控制了阴影渲染的范围。不过因为实例化绘制后draw call 数已大大下降,可以有更多角色有自己的影子啦!


(左:只有1个人有影子;右:四周的人都有影子)

不过阴影范围增加同样也造成了小问题,即阴影的边缘过分清晰,整体效果不佳。

为解决这个问题,我们在显示阴影的着色器代码中加入了淡出的处理,使得整体效果更为自然。 


(左:影子边缘过分清晰;右:影子自然淡出)

此外,实例化绘制也并非都是优化。非实例化渲染有一个优点是视锥体剔除(Frustum Culling) 很容易实现,当人物不出现在相机视线中时,也不会再渲染。

而当人物实例化之后,因为整个模型的边界更难以计算,而且是整体的比较,所以视锥体剔除难以处理,如果想要实现类似单独模型的剔除结果,就需要手动去控制实例的显隐并优化着色器代码来控制开销


灵境至维团队一直致力于打造一个既有人间烟火气,又有无限想象力的虚实共生的世界!而真·千人同屏作为虚拟世界活力的重要基石,技术团队也不会止步于此,会继续去优化它。当然要实现团队愿景,本就需要不断地去挑战这个领域里技术、设计、体验等的众多高峰。虽然很难,但对于我们,你可以期待更多!


编辑:小芸、Neeson、久乔、山童、欧阳

技术顾问:久乔、海青


本文系作者授权数英发表,内容为作者独立观点,不代表数英立场。
转载请在文章开头和结尾显眼处标注:作者、出处和链接。不按规范转载侵权必究。
本文系作者授权数英发表,内容为作者独立观点,不代表数英立场。
未经授权严禁转载,授权事宜请联系作者本人,侵权必究。
本内容为作者独立观点,不代表数英立场。
本文禁止转载,侵权必究。
本文系数英原创,未经允许不得转载。
授权事宜请至数英微信公众号(ID: digitaling) 后台授权,侵权必究。

    参与评论

    文明发言,无意义评论将很快被删除,异常行为可能被禁言
    DIGITALING
    登录后参与评论

    参与评论

    文明发言,无意义评论将很快被删除,异常行为可能被禁言
    800

    推荐评论

    暂无评论哦,快来评论一下吧!

    全部评论(0条)

    作者
    灵境至维

    灵境至维

    广东 深圳

    南山区侨香路4080号侨城坊T7-1505

    行业:营销咨询

    官网:https://www.ljzw.net