元宇宙探索之路

24,573 阅读9分钟

前言

元宇宙正在如火如荼地发展,大有引领未来潮流之势。对于我们这么专业的(web 前端)团队来说,元宇宙是一个大 (wan) 显 (quan) 身 (bu) 手 (dong) 的领域,因此团队在这方面投入了很多人力进行预研和总结,请随本文一起踏入元宇宙的神秘世界。

元宇宙与 3D

元宇宙,或称为后设宇宙、形上宇宙、元界、魅他域、超感空间、虚空间,是一个聚焦于社交链接的 3D 虚拟世界之网络。关于元宇宙的讨论,主要是探讨一个持久化和去中心化的在线三维虚拟环境。此虚拟环境将可以通过虚拟现实眼镜、增强现实眼镜、手机、个人电脑和电子游戏机进入人造的虚拟世界。 以上维基百科对于元宇宙的解释。

相信大家和我一样依然看得一头雾水。或许我们此时还是不明白何为元宇宙,但是由此引出了一个重要的概念—— 3D 虚拟世界。

3D 虚拟世界 这个词,可以拆分成 3 个单词来理解:3D、虚拟、世界。3D 即三维,是指在平面二维系中又加入了一个方向向量构成的空间系;虚拟即使用模型等技术构建的仿实物或伪实物;世界则是由很多虚拟物质构成的事物的总和,即一个个或大或小的虚拟场景。

在元宇宙发展的过程中,涉及到的模型设计制作、场景搭建,都离不开 3D 技术,可以说 3D 技术是元宇宙发展的基石。因此在元宇宙的探索之路上,迈出去的第一步也必然是 3D 技术研究。

3D 技术选型

未入门即劝退的 WebGL

WebGL 是一种 3D 绘图协议,也是一个 JavaScript API,可在任何兼容的 Web 浏览器中渲染高性能的交互式 3D 和 2D 图形,而无需使用插件。换句话说,WebGL 是在浏览器上运行 3D 效果的基础。

但是 WebGL 的入门门槛足够劝退大部分开发者。从最基本的着色器开始,还需要我们去学习图像处理、空间处理、矩阵运算、甚至是几何逻辑等。

我们团队的小伙伴做过一个 WebGL 分享,其中光是实现一个 WebGL 版本的 Hello World,就超过了四十余行代码,更别说代码里需要涉及的概念:

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const vertex = `
  attribute vec2 position;
  void main() {
    gl_Position = vec4(position, 1.0, 1.0);
  }
`;
const fragment = `
  precision mediump float;
  void main()
  {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
  }    
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const points = new Float32Array([
  -1, -1,
  0, 1,
  1, -1,
]);
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const vPosition = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);

所以如果使用 WebGL 从零开始,无疑是非常艰难的挑战。于是我们把目光投向了 3D 引擎。

抱有幻想的 3D 引擎

我们可以把 3D 引擎看成是一个封装了 3D API、图形通用算法、底层算法的工具。通常 3D 引擎都搭配有具备可视化操作界面的编辑器,即便是从零开始,通过创建 3D 类型的节点,甚至只需要拖动编辑器上的 3D 模型,我们就可以快速的搭建一个 3D 场景。相比于晦涩难懂的 WebGL,3D 引擎对于初学者无疑更友好。

Unity 3D

Unity 3D 可以说是市面上使用率最高的 3D 引擎,它具有生态好、功能支持全面、项目优化好等等优点。但是!它可以做到现在的市场规模与地位,隐藏在它背后成功的商业模式功不可没,遗憾的是,它是收费的,而且价格不菲。在没有产生经济效益的预研阶段,我们不希望投入太大的经济成本,因而放弃。

以下是使用 Unity 3D 完成的 Demo 效果:

LayaBox

LayaBox 是一个国产的游戏引擎品牌,旗下的 LayaAir 支持 JS、TS 等语言,且可以兼容使用 Unity 3D 导出的地形、组件、物理引擎、动画、摄影机和粒子等元素,因此一个不成熟的想法油然而生,使用 Unity 3D 编辑然后导出场景,然后使用 LayaAir 绑定交互事件后打包发布,这样就可以完美的避开授权费用了?但是很可惜,我们经过尝试后发现,Layabox 的免费范围也仅针对 IDE 基础功能,对于后边可能用到的 IDE 企业会员专属功能,也是收费的,且官方要求的在首页注明「Powered by LayaAir Engine」,这与我们的商业标准不符,所以也告别了商用的可能性。

Egret

Egret 也是一款国产的游戏引擎,它一开始就专注 h5 开发,在 h5 方面支持较好,但是它原本是专注于 2D 领域的,在 3D 方向起步较晚,很多官方的文档都还不健全,因此上手难度较大,遇到问题只能摸着石头过河,遂 pass。

Godot

Godot 是一款完全免费的游戏引擎,它支持跨平台编辑与发布,但是在打包发布到 h5 页面后,我们发现它打包出来的模型文件较大,这对于移动端加载体验来说是比较致命的问题;而且渲染效果也较为粗糙,模型渲染出现了比较明显的锯齿现象;H5 导出格式支持 WebAssembly 和 WebGL,但是 WebGL 尚不支持任何 IOS 的浏览器。以上种种都不符合我们对元宇宙的预期,因此也只能无奈放弃。

为方便对比,我们做了以下表格进行总结:

引擎名称使用价格脚本语言支持模型格式
Unity 3d每年1800$(个人)C#.fbx、.dae、.3ds、.dxf、.obj
Egret免费TypeScript.obj、.gltf
Godot免费GDScript.obj、.dae、.gltf、.escn、.fbx
Layabox免费TS\JS\AS3.fbx、.dae、.3ds、.dxf、.obj

至此,3D 引擎幻想泡灭。

回首拥抱的 BabylonJS

其实除了上述 3D 引擎,我们一开始就想到的还包括 BabylonJs 和 ThreeJs 这两个主流的 3D 框架。作为市面上比较流行的 3D 框架,它们的文档完善度和学习资源丰富度都没有问题。而在这两者的对比上,我们觉得 ThreeJS 与其说是框架,不如说是一个库,它对 WebGL 进行了封装,将复杂的接口简单化,将对象结构数据化,的确是个不错的选择;而相较而言,BabylonJS 在模块化层面则更清晰,也更像是一个框架,并且它拥有不亚于 ThreeJS 丰富度的学习资源,最终成为了我们团队敲定的技术选型。

开展工作

头脑风暴

作为大促开发团队,我们希望 3D 预研成果能够最终落地到我们的活动。因此在作品定向的讨论上,我们最终敲定了要实现一个虚拟商场。3D 人物模型可以在一个布满各种商品的 3D 商场中行走,它可以运动到心仪的商品前进行预览,甚至可是实现不同 3D 场馆的切换。

素材格式

在明确了作品方向后,我们需要视觉同学提供相关的模型素材。

在众多的 3D 模型格式中,我们最后选择了 .gltf 格式。相对于其他模型格式,.gltf 可以减少 3D 格式中与渲染无关的的冗余数据,从而确保文件体积更小。目前 3D 素材相对来说都比较大,这对于移动端加载体验来说,无疑是致命的。因此拥有更小体积的格式,也拥有了更高的优先选择权。

除此之外,.gltf 是对近二十年来各种 3D 格式的总结,使用最优的数据结构,从而保证最大的兼容性以及可伸缩性,在拥有大容量的同时,支持更多的拓展,比如支持多贴图、多动画等。

所以 .gltf 成为了我们与视觉约定好的唯一素材格式。

开发痛点

  1. 模型边界

    • 问题描述:没有判断模型边界,导致模型可以超过合理范围去放大与缩小。
    • 解决方式:从设计规范出发,开发与设计对齐规范,严格按照统一尺度输出模型。
  2. 碰撞检测

    • 问题描述:没有做好碰撞检测,导致人物模型可以穿透场景模型。
    • 解决方式:除了输出常规显示的模型,还需要输出不用于显示的低模,利用低模来实现碰撞检测,降低碰撞的计算量;添加寻路系统,当运动模型自动行走时,可以自动绕开障碍物模型。
    • 优化前:
    • 优化后:
  3. 场景切换

    • 问题描述:场景切换时,镜头会旋转。
    • 解决方式:切换场景时,需要对不展示的场景关闭控制。需要注意的是,在初始化场景时,通常会伴随着初始化控制,最好在构建函数的最后关闭控制,在当前场景下再开启控制,保证场景控制的唯一性。
  4. 内存开支严重

    • 问题描述:内存占用率大,游戏运行一段时间后,手机会有发热和卡顿等现象。
    • 解决方式:控制内存开销,切换场景时,清空其他场景,避免无效的内存占用。
    • 优化前:
    • 优化后:

作品展示

场景切换:

商品材质切换:

欢迎大家查看链接 预览链接

小结

元宇宙是一个很庞大的概念,此时只是萌芽阶段,正如我们的探索,必然也存在许多不成熟的地方。但我们相信这是未来的一个方向,也相信我们的产品形态会日益丰富与成熟。

让我们共同期待!