中文  |  English

济宁米多信息科技有限公司

Three.js r128 → r185:一个 3D 前端的引擎换代实录

Three.js r185 升级、Three.js 版本升级、WebGL2、颜色管线、色彩管理、three-shim 垫片、去 CDN 化、离线部署 3D、渲染内核换代、浏览器 3D 兼容性

引擎升级是所有 3D 项目里最像"自找麻烦"的一件事。功能都跑得好好的,为什么非要把渲染内核从 r128 换到 r185?这篇把一次真实换代的过程摊开:换之前先算清收益、换了之后逐个解决哪几类断裂、怎么证明画面没被换坏、以及哪些遗留 API 至今还在。这套改造已经做进创世Genesis 的基座;如果你正打算动自己的引擎版本,这篇的顺序和检查项可以直接拿去用。

一、先算收益:这次升级到底买到了什么

升级不是"追新版本",是买三样具体的东西。

第一样:颜色正确性。 r128 时代的默认做法是 sRGB 输出 + 贴图按线性着色,等于一次双重 gamma,画面整体偏亮偏粉——很多团队把它当成"美术调色没调好",其实是管线错误。r152 之后 Three.js 的颜色管理换了机制,r185 走的是正确管线。这不是版本数字游戏,是"同样的模型和贴图,换完之后颜色才对"。

第二样:WebGL2 的能力面。 高斯泼溅的自研着色器、BVH 碰撞、实例化合批,这些都吃 WebGL2 的红利。不换到 r185 就没有这条基线。

第三样:不再依赖外部 CDN。 换版过程中顺手把 importmap 从公共 CDN 改指本地,六个加载器本地化或 stub 化,编辑器群、管理后台、游戏页全部走本地依赖。结果是内网和离线环境也能跑,不会因为外网抖动或版本漂移打不开页面。

顺带还有一条工程收益:换版会把散落在各处的旧 API 调用全部照出来。我们最终整理出79 处遗留调用清单化治理,并用兼容层兜底,让存量用户代码块不必逐个重写。

二、真正花时间的四类断裂

版本号从 128 到 185 跨了 7 个中位版本,加载方式、模块系统、色彩管理、着色器编译路径全都动过。断裂集中在四类。

1. 入口断裂:文件换了名字

最直接的一层:新的 UMD 包换了文件名,老的 import 路径直接 404。做法是用 esbuild 自建一个 r185 的 UMD bundle,顶替旧文件名——对外暴露的入口不变,内部换成新版本。这一步做完,后面的引用一行都不用改。

2. 加载器断裂:六个加载器行为不一致

贴图、字体、GLTF、FBX 等六个加载器在旧版本里各自有依赖假设,有的走全局、有的走模块。做法是逐个本地化或 stub 化,把隐式全局依赖改成显式引用。这里最花时间的是排查"某个加载器偶发失效"——因为它不报错,只是静默走 fallback。

3. 符号断裂:87 个不存在的导出

新版本删掉或改签了一批导出符号。做法是写一层 three-shim 垫片,补上 87 个符号。这层的原则很清楚:只补签名,不补行为——垫片负责让调用能跑通,行为差异由兼容层单独处理,否则两层混在一起,出问题分不清是谁的责任。

4. 语义断裂:颜色 API 换了名字和含义

这是最隐蔽的一类。旧的 outputEncoding、texture.encoding 在新版本里对应 colorSpace,而且不是简单改名——编码值的语义映射有细节。做法是写 patchTHREE 兼容层,把旧属性真映射到新 API,而不是做属性名的别名了事。这类问题不写兼容层就会变成"画面颜色悄悄变了"。

三、怎么证明"换完没坏"

改渲染内核最怕的是:功能都能跑,但画面变了、没人发现。我们的做法是先立基线再动刀。

  1. 10 张统一口径的基线截图:固定机位、固定时间、固定角度,动之前先拍下来。
  2. 自研 PSNR / diff 工具:对基线和新截图做逐像素比对,出 PSNR 值和差异像素比例。
  3. 差异归因:管理后台这类页面实测 43.4dB 一致(结构本来就该一模一样);主世界的差异全部落在颜色上——构图、建筑、角色都像素级一致,颜色变化来自色彩管线正确化,属于预期内。

这里的方法论值得单独说一句:先建基线,再动刀。没有基线,任何"看起来还行"都无法证伪;而有了基线,差异可以被归因到具体成因,而不是靠感觉判断"大概是正常的吧"。

四、顺手做的两件配套

降级要体面。 不支持 WebGL2 的设备不能白屏加一屏报错。做法是 webgl2Guard.js:在加载 Three.js 之前先检测,不支持就全屏中文提示,然后 window.stop() 停掉后续加载——用户看到一句人话,而不是控制台刷屏加白屏。

修掉一个历史缺口。 新版本下 importmap 若同时指向两处来源,会加载出双实例,两份独立的状态机互相看不见,症状是"偶发地某个对象不动"。改指本地之后这条红线消除。

五、指标摆在一起

项目结果
渲染内核r128 → r185,WebGL2,6 阶段全验收,REVISION=185
颜色管线兼容层真映射旧属性到 colorSpace,双重 gamma 问题消除
垫片符号数87
遗留 API 治理79 处清单化 + 兼容层兜底
外部依赖6 个加载器本地化/stub 化,importmap 改指本地,编辑器群 / 后台 / 游戏页全部本地
基线比对10 张统一口径截图;后台 43.4dB 一致;主世界差异全部归因于颜色管线正确化
降级体验webgl2Guard.js 加载前检测,中文全屏提示 + window.stop()

六、这些数字的边界

  • 43.4dB 是管理后台的口径,主世界的差异是预期内的颜色变化,不是"没升好"。别拿一个数当全部结论。
  • 87 个垫片符号只保证调用能跑通,不代表行为完全对齐;涉及行为的部分由 patchTHREE 单独兜底,两层要分开看。
  • 去 CDN 化解决的是"能不能离线跑",不解决模型体积问题。体积属于资产管线(LOD、贴图压缩),不在这次范围内。
  • 79 处遗留治理是清单化的结果,不是"以后不会有遗留"。新版本再升,这一项要重新做。

常见问题

Q:引擎换代要停项目吗?

A:改造集中在加载与兼容层,业务代码基本不动;真正需要集中注意力的是回归验证,因为画面类改动不会报错。

Q:为什么不干脆锁死旧版本?

A:可以锁,但代价是拿不到 WebGL2 的能力面,也拿不到颜色管理修复后的正确渲染。锁版本的代价会随着浏览器演进慢慢涨上来。

Q:旧代码里的旧 API 会被立刻淘汰吗?

A:不会。兼容层保留了旧属性的映射,存量代码块可以先跑起来,后面按清单分批迁移,不用一次性重写。

Q:升级过程中最难判断的是什么?

A:是"没报错但画面变了"。这类问题只有在有基线截图和像素比对工具的前提下才暴露得出来,靠肉眼在正常光照下对比基本发现不了。

源码与仓库

三个地址内容一致,国内访问用前两个更快。仓库里有部署说明与验收脚本。

  • Gitee(国内访问更快):https://gitee.com/miduoxinxijeji/miduo.git
  • GitCode(国内镜像):https://gitcode.com/qq_35054471/virtual-world
  • GitHub:https://github.com/miduo100/3d-virtual-world

关于创世Genesis

创世Genesis是一套基于Three.js+WebGL构建的自部署3D虚拟世界系统,帮助个人与企业搭建属于自己的3D空间。浏览器直接访问,PC和手机双端兼容,支持多人在线、联邦传送、商铺系统,并支持Agent接入——AI能以具身角色进入你部署的世界。数据运行在你自己的服务器上,不经过第三方平台——让每个世界都真正属于它的主人。

打算给自己的 3D 前端换一次渲染内核? 创世Genesis(创世虚拟世界CRM系统)是一套部署在你自己服务器上的 Three.js 3D 虚拟世界基底——r185/WebGL2、颜色管线、加载器本地化、兼容层、降级提示这些不性感的部分已经铺好,你写上面那一层就行。官网(搜「创世虚拟世界CRM」即可找到)有可以走一圈的演示世界。

关于名字:本文说的创世Genesis,即创世虚拟世界CRM系统,两者是同一个自部署 3D 虚拟世界产品。若你通过「创世Genesis」没搜到我们,直接搜「创世虚拟世界CRM」即可。
← 返回文章列表