当前位置: 首页 > news >正文

拥抱新一代 Web 3D 引擎,Three.js 项目快速升级 Galacean 指南

作者: vivo 互联网前端团队- Su Ning

本文从多个维度对比 Galacean 和 Three.js 两款Web3D 引擎的差异,并介绍拟我形象项目从Three.js 切换到 Galacean 以后带来的提升以及项目迁移的心得,为其他 Three.js 项目升级到 Galacean 提供参考。

1分钟看图掌握核心观点👇

一、背景

Web 3D 技术的发展日新月异,为我们带来了前所未有的沉浸式体验。从虚拟展示到游戏开发,从建筑可视化到教育模拟,Web 3D 技术的应用场景愈发广泛。而在这一领域,Three.js 作为一款广受欢迎的 JavaScript 3D 库,凭借其简洁易用的 API 和丰富的功能,帮助众多开发者实现了精彩的 3D 项目。

然而,随着项目复杂度的不断提升,以及用户对性能和体验要求的日益苛刻,Three.js 逐渐显露出一些局限性。比如在处理重负载时,很容易遇到性能瓶颈,出现卡顿、掉帧等问题。这就如同一位经验丰富的车手,驾驶着一辆曾经性能卓越的赛车,但在面对愈发复杂的赛道和激烈的竞争时,却发现车辆的动力和操控性渐渐力不从心。

二、Galacean:新一代 Web 3D 引擎

2.1 业务简介

拟我形象是 vivo 账号中的一个3D数字人功能,提供一种代表自由、个性、创新和时尚的虚拟形象,为用户提供更加生动、直观、有趣的交流方式。采用 Native+H5混合的开发方式,其中 3D 渲染的部分基于 Three.js 进行开发。

2.2 技术挑战与痛点

  • 性能瓶颈: 人物模型包含大量形态键以实现多样化面部特征,导致模型加载解析耗时过长。

  • 线程阻塞: 受限于JS单线程特性,模型解析过程会造成页面短暂无响应。

  • 多模型渲染: 套装切换等场景下,多个模型同时渲染时性能问题尤为突出。

  • 阴影优化: Three.js 的阴影渲染性能消耗大,不得不通过局部阴影和限制捕捉范围等折中方案来平衡画质与性能。

2.3 Galacean 引擎核心优势

Galacean 是一款开源的 Web 游戏引擎,致力于打造一个开放、易用、高效的游戏开发工具,可以通过在线编辑器或者纯代码的形式进行使用。

针对现存的技术挑战与痛点,Galacean做了深度优化:

  • 多线程处理: 采用Worker避免主线程阻塞。

  • 移动端适配: 对大量常量进行近似取值优化,完美适配移动端。

  • 性能突破: 优化数据传输链路,创新缓存设计,显著降低重负载场景下的卡顿现象。

在这里插入图片描述

对比视频1:加载速度
在这里插入图片描述
对比视频2:套装切换

此外,Galacean 基于 EC(Entity-Component)架构设计,而非 Three.js 的面向对象,大幅提升了开发的灵活性。

近期我们将渲染引擎由 Three.js 切换为 Galacean。这一举措不仅解决了页面卡顿问题,还提升了浏览器兼容性(可支持到 chrome82),帧率表现更出色,画面质感也得到显著改善。整体切换过程较为平滑,但也遇到了一些问题。接下来,将与大家分享此次整体升级的相关经验。

三、调优过程

任务拆解:

作为一个数字人项目,涉及到引擎升级的模块大致有

①环境初始化(场景、相机、光线、引擎设置)

② 模型加载

  • 骨架获取

  • 材质获取

  • 动画获取

③妆容、穿搭还原

  • 形态键修改

  • 贴图、颜色修改

  • 模型替换

  • 头像(静态头像、动态头像)导出

  • 壁纸(静态壁纸、动态壁纸、视差壁纸)导出

经过梳理,可以大致分为四类:

  • 初始化

  • 模型加载

  • 素材替换

  • 动画状态

接下来我们对这几个部分进行分别的处理

3.1 初始化

有别于 Three.js 的渲染器创建,Galacean 的 engine 初始化是异步方法,所以后续用到用到engine的地方需要考虑加载的时序,以及engine存在状态的判断。另外,Three.js 中 renderer 的渲染行为需要手动调用,一般是使用requestAnimationFrame循环调用,而Galacean则不需要,引擎开始渲染只需要调用一次 engine.run 即可。

const renderer=new THREE.WebGLRenderer({alpha: true,antialias: true,
})
document.body.appendChild(renderer.domElement)
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(15, window.innerWidth/window.innerHeight, 0.1, 100)
requestAnimationFrame(function render() {renderer.render(scene, camera)requestAnimationFrame(render)
})
const engine = await WebGLEngine.create({canvas,physics: new LitePhysics()
})
engine.run()

在 Three.js 中,尺寸单位统一以米为基准,无需额外进行特殊处理。不过在角度单位的使用上存在差异:Three.js 里,仅相机的 fov(视场角)采用角度单位,其他涉及角度的参数均以弧度计量;而 Galacean 则采用更为统一的设定,所有角度相关单位均为角度。

/** Three.js */
camera.fov = 15
item.rotation.y = 15 * Math.PI/180/** Galacean */
camera.fieldOfView = 15
item.rotation.y = 15

在Three.js中颜色的设置更加灵活,可以使用16进制或者RGB值来进行赋值,但是在Galacean中只能通过RGB来进行赋值,且有别于0-255的取值范围,Galacean中的颜色范围是0-1。从Galacean1.5版本开始,默认的色彩空间改为线性,在代码中需要手动转换一下。

/** Three.js */
directLight.color=0xffffff
directLight.intensity=0.9/** Galacean */
const color = new Color(0.9, 0.9, 0.9, 1)
color.toLinear(color)
directLight.color = color

3.2 模型加载

对于包含大量形态键和动画的模型,将模型打成zip包可以有效的压缩模型的体积,不论是Three.js还是Galacean都不支持加载zip包,但是我们可以自行扩展模型加载的链路,将zip下载后解压出的模型获取ObjectUrl再放到各自的加载器中加载,这样加载进度的获取也可以进行自定义,不需要进行额外的改造。

exportclassModelLoader {engine: WebGLEngineconstructor(engine: WebGLEngine){this.engine = engine}async load(src: string) {const url = await fileLoader(src)returnthis.engine.resourceManager.load<GLTFResource>({url,type: AssetType.GLTF})}
}

Three.js 解析 glTF 模型输出的数据结构较为简单,主要使用模型的场景和动画片段。由于后续需针对特定材质进行替换,所以要根据节点名获取特定节点,再取出节点中的材质信息,模型的骨架也通过这种方式获取。而 Galacean 输出的数据更为全面,除动画片段和实体信息外,模型中使用的材质、贴图、蒙皮和网格信息也会分门别类展示,需要对应内容时直接获取即可,相比 Three.js 更加方便。

3.3 素材替换

素材替换如上文总结分为四种,分别是颜色、贴图、形态键和模型的替换,颜色设置我们在初始化中已经讲解,而模型加载和展示也没有特别的内容,无非是节点/实体的添加和移除,这里我们讲下贴图和形态键修改的一些tips。

在Three.js中修改材质贴图map可以直接直接使用canvas或者image,修改后需要将材质needsUpdate属性设置为true。而在Galacean需要先将图片加载为texture,再进行赋值。

/** Three.js */
material.map=canvas
material.needsUpdate = true/** Galacean */
const texture: Texture2D = await engine.resourceManager.load({url,type: AssetType.Texture2D
})
material.baseTexture = texture

在Three.js中修改形态键,可以先通过网格中的morphTargetDictionary属性获取到需要修改的形态键的索引,然后修改morphTargetInfluences中对应索引的值即可。

在Galacean中网格渲染器中没有存储形态键的索引信息,而是存储在MeshRenderer下的mesh属性下的blendShapes属性中,通过获取对应名称的形态键在数组中的索引,修改网格渲染器中blendShapeWeights属性对应下标的值。

/** Three.js */
const index = morphTargetDictionary[keyName]if (index !== undefined) {mesh.morphTargetInfluences[index] = value
}/** Galacean */
const blendShapes = skinMeshRenderer.mesh.blendShapes
const index = blendShapes.findIndex(i=>i.name===keyName)
if (index > -1){skinMeshRenderer.blendShapeWeights[index] = value
}

3.4 动画

相较于Three.js的AnimationMixer和AnimationClip,Galacean拥有更加完善的面向组件的动画系统,支持 状态机、混合动画、时长压缩等,不同动画之间的切换与播放更加简单易维护。

/** Three.js 播放动画片段 */
const mixer = new THREE.AnimationMixer(scene)
const action=mixer.clipAction(avatarClip)
action.play()
ticker.addEvent(delta => {mixer.update(delta)
})/** Galacean 添加状态机,播放完成回到待机状态 */
const animationState = animator.findAnimatorState('action')
const idleStatle = animator.findAnimatorState('idle')
const transition = new AnimatorStateTransition()
transition.duration = 1
transition.offset = 0
transition.exitTime = 1
transition.destinationState = idleStatle
animationState.addTransition(transition)
animator.play('action')

四、结语

Galacean 的出现,无疑为 Web 3D 开发领域带来了新的活力。它不仅解决了 Three.js 等传统技术在性能和功能上的诸多痛点,还以其卓越的性能、丰富的功能和易用性,为开发者打开了一扇通往更广阔创意空间的大门。

需要注意的是,Galacean不同版本之间的API差异较大,需要进行甄别,同时开发文档及相关的案例也需要进一步完善。

对于全新的项目,Galacean提供编码或在线编辑器两种方式保障创意的高效落地,详细的文档和案例也便于接触 Web3D 开发的新人快速上手。

对于存量的项目,Galacean的迁移成本不高,且整个过程平滑可控,能够有效提升现有项目的画面表现和性能。为未来复杂度更高的需求提供性能保障。

http://www.dtcms.com/a/389191.html

相关文章:

  • Linux 内核裁剪与功能扩展实验报告
  • Qt QVCandlestickModelMapper详解
  • LeetCode:20.旋转图像
  • 网络协议深度解析:从OSI七层模型到现代互联网通信的技术实战
  • 慈明学校以孝治家阳光家庭教育中心 学以致用践行以孝治家幸福万家
  • 开心实习之 深度学习之多层感知机
  • 前端构造数据格式及表格添加行
  • 深度学习-神经网络(上篇)
  • 【脑电分析系列】第18篇:传统机器学习在EEG中的应用 — SVM、LDA、随机森林等分类器
  • 理解长短期记忆神经网络(LSTM)
  • Kurt-Blender零基础教程:第2章:建模篇——第1节:点线面的选择与控制与十大建模操作
  • 鸿蒙5.0应用开发——V2装饰器@Monitor的使用
  • 八、Java-XML
  • 计算机在医疗领域应用的独特技术问题分析
  • HTB Intentions writeup(SQL二次注入也是注入)
  • 第一章 预训练:让模型“博闻强识”
  • 【数组】求两个匀速运动质点的相交或最小距离
  • 新手向:Python爬虫原理详解,从零开始的网络数据采集指南
  • OKZOO进军HealthFi:承接AIoT,引领Health-to-Earn
  • Halcon 相机标定
  • 腾讯混元发布集成翻译模型Hunyuan-MT-Chimera-7B,已开放体验
  • mybatis-plus扩展
  • 从x.ai到VSCode:一个AI编程助手的意外之旅
  • SQLite vs MySQL:核心SQL语法差异全面解析
  • 【每日算法】两数相加 LeetCode
  • ActiveMQ底层原理与性能优化
  • Ceph IO流程分段上传(1)——InitMultipart
  • 大数据毕业设计选题推荐-基于大数据的农作物产量数据分析与可视化系统-Hadoop-Spark-数据可视化-BigData
  • 【回归之作】学校实训作业:Day04面向对象思想编程
  • Ubuntu20.04或者Ubuntu24.04 TypeC-连接屏幕不显示问题