声网管浩森:元宇宙派对场景的最佳实践
前言
元宇宙场景在这两年逐渐兴起,成为很多社交、娱乐产品关注的新场景。而且,目前有不少产品已经落地。本文整理自声网解决方案架构师管浩森在 RTE 2022 大会的演讲。他是元宇宙方向的专项解决方案负责人。他基于协助客户落地元宇宙场景的经验,以元宇宙派对为例,分享了该场景的实践经验。
本文内容基于演讲内容进行整理,为方便阅读略有删改。
大家好!我叫管浩森,是一名开发者也是一名创业者。我在连续创业过程中对 toC 产品和功能,包括架构设计方面积累了丰富的经验和看法。2021 年我加入了声网,目前担任元宇宙方向专项解决方案的负责人,目前致力于为客户提供市场上更有优势整体解决方案。
今天我们来讲一个比较轻松的话题,“如何在元宇宙里创建一场派对”。我会以元宇宙派对场景作为基础,介绍从前到后、从里到外整套的解决方案。
我们先看一下元宇宙场景目前的发展。首先,我们看一下近年来社交场景形态的变更。从“在线”到“在场”,消费者的社交形态其实是不断升级的:从 2011 年移动端及时通信的兴起,随着智能手机的普及,手机 QQ 等产品带来移动文字即时通讯,也就是 IM,包括离线留言的功能,类似于 QQ 空间。当时它的社交方式、社交属性实际上是通过表情包+文字的方式进行即时通讯。
时间来到 2016 年,全屏短视频开始爆发。随着抖音在 2016 年的发布和快手的推广,人们逐渐习惯了通过分享和评论去观看 UGC 的内容,也就是用户自己在平台上发布的视频产品。对于此类产品,社交形态主要通过录制的视频+录制的声音和用户的评论来交流。
2018 年,直播场景全面爆发。游戏风口当年正热,短视频、直播逐渐成熟,直播带货在直播行业中全面兴起,对于直播而言此时交流方式变成实时的影像+实时的声音。
2021 年,Facebook 在年底改名为 Meta,像一个重磅炸弹一样为整个行业炸出了一条路,电影、VR、游戏在这一年全面开花,让元宇宙概念再次火热起来。在元宇宙元年,人们之间的交流方式已经由实时的影像+实时的声音变成了实时渲染的场景+实时的音视频。
对于消费者在元宇宙的了解这一步来看,我们找到了以下的数据。调查显示,我们对元宇宙了解程度普遍较高,因为元宇宙一词近年来接连被提及,特别是疫情情况下对于新型社交类型的强需求,Facebook 的改名、微软收购动视暴雪、百度推出了希攘,这一系列的信息让网民对于元宇宙的概念已经有了一个基本的认知环境。经数据调查,所有受访的网友都听过元宇宙的概念,其中 61.4% 的网友对于元宇宙比较熟悉,15.9% 更是非常了解,对于元宇宙消费者有什么期待呢?
数据展示,网友更多期待元宇宙社交的场景。国内泛娱乐快速发展和互联网浪潮双重推动下,国内的网友,特别是 Z 时代年轻人,对于新兴事物接受程度和尝试意愿都非常高。根据数据调查显示,消费者对于元宇宙的期待还优先在泛娱乐场景,特别是虚拟社交领域超过 60% 网友比较愿意尝试元宇宙类型的社交模式。
既然如此,我们该怎么样搭建出来一个社交元宇宙场景呢?接下来跟着我一起开一个元宇宙的派对。
01 创建元宇宙:万物生长
首先我们先看创建元宇宙第一块万物生长,所谓万物生长即我们要在元宇宙整个场景内创建山川河流、世间万物,如何创建这是第一道难题。
首先我们讲一下对于模型的构建。模型构建现在市场上已经有了一套成熟的流程,通过一系列的步骤,比如说角色设计、中模、高模、低模、烘焙贴图、骨骼绑定等操作,便可以完整地建立一套元宇宙场景下的人物模型。当然也有整个场景的虚拟,现在还有很多的模型是可以直接购买使用的。
此外可以通过引擎和底层 API,以捏脸的形式做业务层的换装,类似于苹果的 Memoji,在建立标准模型基础上可以提供给真正 C 端用户更个性化人物定制和形象定制,声网行业会逐步完善地推出 MetaLive 产品包,让大家可以用 Native 的开发方式实现换装的功能。
第二块,我们现在有了一套虚拟场景,我们怎么才能让它动起来?对于动起来最重要的是模型间交互的脚本制作。目前模型交互脚本制作有两个方案可以选择,一个是使用现成渲染引擎,比如 Unity、Arnold 第三方渲染引擎,也可以通过 OpenGL、Direct3D 底层 API 自己去控制底层的硬件和渲染整个场景。声网在这方面会提供 Metachat 等合产品包,来帮助大家实现整个场景的渲染部分逻辑,让客户更有精力地关心业务部分的逻辑。因为我们知道不管是元宇宙还是 2D 社交产品,它的派对或者社交本质还是人本身。
02 创建元宇宙:推杯换盏
我们怎么才能在元宇宙的场景中互相交流,在排队中推杯换盏?下面跟我们一起走进一个场景内,看看整个元宇宙是如何活起来的。
首先,我们看一下信令控制,包括所谓场景控制,现在创建了 3D 虚拟世界,我们该如何进行操作呢?元宇宙内会有大量的信息、信令交互,比如说我的位置坐标、我给谁送的礼物、我的动作什么样,随着房间内人物越来越多,服务端对于这类信息处理的压力也会越来越大,对此,基于给多个客户的打磨,声网有以下的经验和一些推荐的思路,分享给大家。
第一,是将不同的数据分为不同的频次,比如我现在要同步每个人位置信息坐标,位置信息同步很有可能是每秒 10 次甚至每秒 30 次的帧率,如此庞大数据对于服务器存储是无意义的,它只是一个可丢失、只需要保序的信令。对这类信令我们要和低频次的数据进行分离,比如说我去给谁送礼物,这属于一个低频的数据,不同的数据通过不同的通道进行同步,这样可以有效降低服务器的压力。有一些高频的数据没有必要存储,有些低频的数据包括重要信息是适合存储的。
第二,将同步数据按照重要类型划分。我们把它分为了关键帧和非关键帧两类。关键帧一般指事件的同步,比如开闭麦的状态、房间内的属性。就比如我现在在使用电脑,别人没有办法随意把这台电脑给抢走。非关键帧指的什么?类似于我刚刚说对于整个场景内位置信息的变动,包括动作细节的模型、面部结构化数据,整个脸在此刻动的结构化信息,这个结构化信息是没有必要每一帧都完整传输,它和视频帧是类似的情况。对于此类信息需要保证快速且保证时序,但不一定能够保证必须到达。对于此类数据我们把它划分为关键帧和非关键帧进行传输,关键帧更多需要记录,而非关键帧更多需要保证速度。
第三,是统一的思路,对于消息风暴的规避。如果频道内的信息过多很有可能造成服务中整体压力包括客户端的回调压力。对此,一般业内会这样处理操作,比如 A 和 B 两个人,他们之间同步信息不是 A 广播自己所有的信息到频道内,而是 A 单独发给服务器,B 也单独发给服务器,服务器整合了之后通过每秒 10 次或者 20 次的频率统一下发给各个端,这样每个端收到的消息不再是所有人的信息叠加,而是服务端整理好整合的信息,这样可以规避大量的数据仿造。
其中,第二部分我们需要明确 RTM(Real-Time Messaging)几个功能模块,我简单介绍一下,因为接下来整个场景我会主要以声网 RTM 功能来作为类型的串联,大家可以类比把声网 RTM 替换成大家现有使用的产品。
声网 RTM 主要的功能模块有这么几个:
频道信息,是作为广播性质的消息,常常作为非关键帧的广播。
频道属性,频道属性是长期存储的,大家可以以自己业务服务器来替代,频道属性每一次变更后,所有人都会收到频道属性变更信息。
用户属性,用户属性是各个用户之间不会默认的订阅,需要订阅某个人之后,某个人的用户属性变更我才会收到变更信息。
接下来,我主要会通过以下三个互动场景作为重点,来给大家介绍一下在元宇宙场景中这些场景的注意事项和实践的方式。
第一就是**“你在哪儿**”。我们现在进到了一个场景内,我们怎么才能移动?我会以人物移动为例,讲解关键帧和非关键帧的同步策略,此时关键帧和非关键帧同步定义偏向于是否进行存储。
第二就是**“打招呼**”。我能移动了之后,我身体各个动作该怎么处理,我见到了同事之后、见到了同学之后,如何才能以一个方式告知让我看到我在和他打招呼。
第三是元宇宙场景内经常被忽略一个细节,就是**“先来后到**”,冲突的处理和锁的处理,对于此我会以抢椅子场景为例介绍锁的概念和其应用。
接下来我们代入一个场景(如下图所述):
对于此方案最主要作为目标位置移动同步信息,对于坐标位置同步我们一般分为两大类,一类是通过点击屏幕或者按钮进行操作,类似于下图展示的游戏,大家很多人玩过《梦幻西游》,对于这类游戏大家可以理解为我是点击某个位置之后,人物通过自动选址走到那个位置。
对于这种情况,我们知道开始位置信息和结束位置信息。此时,开始位置点和结束坐标位置点作为两个关键帧会传输给各个端,各个端收到了关键帧之后,就不可以通过每个端一致选址策略,来实现每个端走的动作是一样的,这样实现了伪同步。我需要同步的信息量很小,因为不需要同步每一个位置帧,我只需要同步两个关键帧即可。
当然,这个策略也会有一些弊端。我只适用于“通过点击屏幕或按钮操作”,即通过指令开始和结束位置的操作,而通过轮盘或者手柄方式进行自由移动的场景,则需要考虑下面这种策略。
另一类则是通过轮盘或者外设的方式,自由控制角色进行走动,此类方式没有固定的结束位置,无法通过关键帧的方式进行广播,需要一个信令通道进行非关键帧(位置坐标)的同步,在用户移动时,快速(例如每秒 10 次)向频道内发送消息,收到广播消息的用户拿到对方坐标位置后做位置渲染。非关键帧信息可以不被服务器记录。
同时,为了服务器可以定时存储角色当前位置,可以定义例如每 5 秒作为一个关键帧存储在频道属性内/服务器内,此时若角色掉线,则重连后可以渲染回到上一次的关键帧位置。
那么这两种在元宇宙中的操作,需要怎么实现呢?下面以 RTM 使用为例,给大家介绍一下整个使用流程。
1.如果通过指令开始和结束位置需要三步。第一加入 RTM 房间,通过获取到开始和结束后的位置信息,更新 RTM 的房间属性,房间属性变动之后同步广播给房间内所有人,所有人拿到了远端状态信息,可以通过本地处理和选址渲染出来位置变动人的移动方式,这样每个人就可以看到对方在移动了。
2.通过自由移动位置坐标方式去进行移动的话该怎么操作呢?右边部分各端加入 RTM 和 RTC 房间内,用户移动的时候可以通过 RTC 的 data storage 通道或者说 RTC 频道信息通道发送关键帧,此关键帧可以每秒 10 次或者每秒 25 次,和帧率保持一致情况,向频道内发送我自己当前的位置消息。远端的客户收到了我的频道消息,我的非关键帧之后,通过自己的渲染,渲染出来我当前每一帧的位置信息,然后就可以渲染出来我的坐标了。此外,我们还需要定义一个定时器,比如说 5 秒一次,把当前状态同步到房间属性或者服务器里面做存储,这样为了完善元宇宙内离线重连业务层服务体验的问题。
那么接下来,我们要解决如何在元宇宙场景中 Say Hi 的问题。
大家可以知道我希望和他打招呼,为什么此时我动不了?因为打招呼本质是信令的出发。我作为 A,我的舍友作为 B,A 和 B 打招呼,信令是 A 把打招呼动作指令发送给 B,同时 A 和 B 同时渲染打招呼的动作,此时 A 和 B 可以同时看到我和他在招手。我们对这些指令有一些定义,比如说我们希望被某些人看到,这样就可以通过刚刚说用户属性变更,大家还记得用户属性和频道属性之间区别是什么吗?用户属性是默认不被所有人订阅的,只有订阅自己的人才能看到自己的变动,我打招呼只有想看我的人,或者离我足够近的人才可以渲染出来我打招呼的动作,离的远的人不用渲染此动作了,这样就会极大减少客户端的渲染压力。
或者说我希望只让部分人可以看到打招呼的信息,此时我就可以通过点对点的消息,例如 IM 或者服务器的 socket,让部分人主动渲染出来我打招呼的信息。
此时有一个比较重要的点,我打招呼并不是我主动渲染之后别人就可以看到,而是我通过信令让别人知道了之后,各个端渲染远端人打招呼信息,这样才是一个正确的方式。当然也可以通过业务层的限制,我给所有人去广播,但是某些人是不会去渲染的,这样就需要业务层服务器去做权限的控制或者数组的定义。
流程大家可以看一下右边,各端先加入 RTM 房间,角色 A 触发了打招呼的动作,触发是让我自己看到我自己在和对方打招呼。我打招呼的同时通过属性的变更或者频道的消息,广播给房间内的其他人,其他人收到了之后大家也渲染出来我和其他人招收的动作,此时就完成了打招呼的动作,包括之后一系列的碰杯、拥抱、送花都是通过此类方式可以去执行。
对于此类信息我们有一个先来后到的概念,如何处理冲突实际上是元宇宙场景内一个比较容易忽略,但是非常重要的一环。
这样的冲突声网将椅子的占有权定义为一把“锁”,这把“锁”的占有权是只能属于一个人。我如何才能处理整个冲突的过程呢?比如我是 A 角色,另外一个兄弟是 B 角色,我们两个同时想坐在椅子上,需要先尝试获取此“锁”的占有权,RTM 服务端会按照到达的顺序去判断,或者我们在业务服务器一层去处理优先权,比如我是 VIP,我肯定要比对方先占有此座,除去这个以外是论时间到达先后。RTM 会自动处理分布式的冲突,谁先到达了之后,会返回一个“请求锁成功”的回调,对方会反馈一个失败的回调,收到此回调之后,我们才会处理坐下动作脚本。坐下动作脚本和打招呼动作脚本实现原理是一致的。
这一点其中比较重要的是,我需要去获取“锁”成功的回调事件之后才会触发动画脚本,而不是我点击坐下按钮之后就会触发,这样就会出现我在刚才场景里描述,我们两个同时都坐在了椅子上,并且串模了。这样 B 角色收到自己无比坐下或者获取锁失败的操作之后,B 就会通过客户端弹窗告知此座位已经有人了,你无法坐下了。同时,声网把椅子占有状态作为房间属性储存在服务端或者储存在房间信息内,这样每当其他人靠近椅子的时候,会感知到这个椅子上已经有人了,这样就不会再出现坐下的按钮去重复进行“锁”的获取。
整个流程大家可以看右边部分,第一块就是加入 RTM 房间内,角色 A 去申请椅子占有“锁”,如果返回失败的话进行弹窗提示没有此类信息,如果返回成功的话会触发坐下椅子的操作。
坐下椅子同时回到打招呼整个流程内,我坐下的动作触发了之后,会通过一个频道消息发送给各个端,各个端也会渲染坐下的指令,这样大家都可以看到我坐在这把椅子上。角色 A 站起来了之后,会通过 release 的方法释放对椅子锁的占用,这样其他人在进来的时候就可以再次坐在这把椅子上了,这个就是一个冲突的处理。
我该怎么在元宇宙场景里像真实世界一样交流,声音它都去哪儿了?我们来进入第三个章节,创建元宇宙的空气和水。
03 创建元宇宙:空气和水
实时音视频的引入会使得元宇宙的社交场景更具有沉浸感。角色间语音的交流,场景内背景音乐的播放、和好友一起边看电视边聊天、甚至通过空间音频判断对方的位置或者距离,这都会让元宇宙体验更上一个台阶,实时音视频在元宇宙场景中就好像空气和水一样,大家注意到它,但它却是无处不在。
我会主要把实时音视频划分三个模块进行派对内场景的分析:
第一块会以一起看电视为例介绍如何在元宇宙房间内实现一起看、一起听或者说 BGM 播放同步功能。
第二块我会以一个游戏的方式,我闭着眼捉迷藏,如何通过声音的方向去判断其他人的位置,空间音频的引入其实就可以像真实世界一样,带给元宇宙更深层次的沉浸感。
第三我会以悄悄话或者心里话为主,介绍如何通过声网最新 SDK 多频道方式进行不同场景融合和交流私密性的保证。
接下来我们进入场景内,一起看或者一起听实际上为的就是保证每个人看到时间点是同步的概念,这样大家可以保证同时感受到相同的气氛,甚至可以实现合唱的场景。
如何才能通过声网 RTC 功能同步一起看或者一起听的时间进度?声网给出了以下三种方案:
1、通过【播放者】MPK 本地播放后推流到频道内
第一是通过播放者的概念,把某一个客户端播放的视频推到频道内。声网为一起看一起听推出了适配 MPK(Media Player Kit)媒体播放器。MPK 媒体播放器不仅可以播放音频视频文件,包括 CDN 媒体流,甚至可以将本地播放的视频以 RTC 视频流的形式推到声网频道内。此时大家通过 RTC 强同步进行时间的同步,而不需要去考虑时间戳的对齐,这样是较好的方式。但是此方案需要有一个具备推流权限的客户端作为发起者,自己播放的视频将会推到频道内,具体流程大家可以看下图。
业务端首先发起我要播放的请求,业务端服务器告知你可以播放之后,客户端 APP 去播放此视频,并且把播放的视频流推到声网大网内,声网再通过转发,转发给各个不同播放端进行视频共同播放,这是第一种方式。
2、通过【发起者】同步进度,各端自己播放
第二种方式可以通过发起者的同步进度,各个端自己通过 RTM 方式播放此。方案就不需要通过 RTC 作为强同步的标准,而是通过声网提供 data stream 通道。你可以用这个通道将某一个发起者作为时间戳的主体,将发起者当前播放时间进度同步到频道内,各个端基于播放者或者发起者角色时间播放进度来拉齐自己当前的进度。
与第一种方式的不同,各端拿到进度之后进行自己播放器时间戳对齐。这种会有一个问题。每一次传输的物理延迟是不一样的,第一次发送 data stream,我和 A 和 B 物理延迟就是链路延迟是 50 毫秒,下一次是 70 毫秒,这样相差 20 毫秒对于本身时间戳进度感知不强,但如果对齐会有卡顿的感觉。所以要引入预值缓冲区的概念。如果大于预值缓冲区的预值(如 100 毫秒),比如说这一次 50 毫秒,下一次 70 毫秒,下一次变成 40 毫秒,都在预值波动内,这样大家可以通过在播放内,不用对齐。超过了此进度的话,比如当前在 4 分 10 秒,我刚进来的人是 0 分 0 秒,4 分 10 秒减 0 分 0 秒肯定大于 100 毫秒,这样才可以进行对齐,把 0 分 0 秒那个人进度拉齐到 4 分 10 秒。具体情况大家可以看上图。其实和第一种方案是一致的,只不过每个端自己去播放,通过 MPK 去播放视频。
3、通过 CloudPlayer 播放后推流到频道内
前两种方案大家可以发现一个弊端,都需要通过发起者的概念去维护整个进度或者整个视频,如果发起者的网络条件不太好或者性能达不到的话,会影响整个房间内所有人的视频播放体验。
对于此声网提出了 cloud player 的概念,业务端可以通过 RESTful API 调用,服务端无需加入某个频道内,就可以去开启一个 worker,把需要播放的数据推到声网的大网。声网大网通过视频流的形式,保证同步,并推到各个播放客户端。此方案就可以规避掉房主或者发起者的概念,所有人可以任意进出此房间,但是我在房间内屏幕上播放是一直进行的。
以上三种方案都可以解决一起看的功能。
对于此,声网引入了一个空间音频的概念,空间音频概念可以让沉浸感更进一步。通过 RTC 音频、声音辨别出用户的方位,可以为元宇宙场景提供更好的体验。声网提供空间音频可以让用户通过音频感知到说话人的方向、位置高低、距离远近的变化,同时还模拟了空气的衰减,音障,比如我和你隔了一道墙,你说话和没有隔一道墙说话是不一致的一个体验。对此,其实可以丰富真实环境的变化,包括带给元宇宙场景内的用户更真实的体验和更丰富的玩法。
声网目前空间音频还是关注 RTC 音视频本身的变化,比如人说话的声音,而对于场景内本身的脚步声、关门声、环境声效需要业务层在渲染引擎层面、脚本层面自己去处理。
对于空间音频的流程如下:
1.加入 RTC 频道【频道场景为直播,身份为主播】
2.muteAllRemoteAudioStreams(true)【默认不收流】
3.创建并初始化空间音频对象并设置空间音效的参数
4.先后调用 updateSelfPosition 和 updateRemotePosition 来设置远端和自己的位置
5.clearRemotePositions 取消空间音频效果,destroy 销毁 AgoraLocalSpatialAudioKit 对象
首先需要加入 RTC 频道,同时把所有人远端的人关闭收流,相当于把开关音频权限交由空间音频处理。
第三块是创建空间音频的对象,并且设置空间音频的参数。这时候我们可以调用自己在整个世界坐标内的位置坐标和设置远端的位置坐标,在本地渲染出来我和他的距离、我和他的相对方向、我和他的高低,这样就可以在本地去渲染出来每个人的位置坐标和空间音频的效果。
声网提供此方案不会影响远端传输,所以每个客户端都可以自由定义每个音频的具体位置。
对于在元宇宙中说悄悄话,或者说小范围交流的场景需求,我们引入了多频道的概念。声网新版的 SDK 允许用户通过自由操作多个频道来实现多频道的推流和拉流。比如在当前场景内,我们可以通过三个频道来实现整个元宇宙派对的概念,首先乐队演奏可以拎出来单独做一个频道,所有人都可以订阅,但是所有人在此频道内不发流,只有乐队可以发流。这样大家可以同时听到同一首背景音乐,有共情的感觉。如果大家希望也可以把乐队当做空间音频去处理。
第二个频道就是在大厅场景内,所有人都可以听到嘈杂空间音频的声音,比如说离我 10 米距离某个小兄弟说话声音会比较小,离我比较近的人说话声音比较大,让场景更贴近现实。
第三块是刚才说通过卡座或者小分队的概念,可以作为单独频道,此频道只允许某些用户可以被加入,如果此频道内的用户说话不会被大厅内其他用户听到,这样既可以保护到隐私也可以同时听到整个大厅内的音频发送,既可以体验到整个场景自由度也可以保护自己的隐私,就类似于频道的卡座,甚至看球的时候 VIP 专座一样。
除此之外,用户甚至可以将频道与一起看等结合,在某个频道内单独去观看某个内容,这样都交由开发者自己去处理。
以上就是我们搭建一个元宇宙派对场景的实现过程与原理。如果大家感兴趣,希望查看元宇宙相关的声网解决方案,请点击此链接。
评论