给浏览器端 3D 虚拟世界做资产盘点:3.1GB 瘦到 390MB,剩下 79.3MB 为什么先不动
3D 资产管理、孤儿资产清理、GLB 资产优化、3D 虚拟世界部署、glTF BIN 缓冲、资产审计、跨世界加载速度、Three.js 资产管线、3D 模型瘦身、自部署 3D 世界
一个跑了几年、不断在改的 3D 世界,资产目录几乎必然会长出"垃圾":上传了又没用的模型、试过又删掉的贴图、改了元数据却没重建的二进制。它们不报错、不白屏,只是安静地躺在服务器上,让每一次打包、备份、跨世界加载都背着本不该有的体积。
这篇讲我们对自己这套浏览器端 3D 虚拟世界做资产盘点的过程:怎么把"孤儿资产"找出来、找到了多少、清掉了多少,以及——为什么剩下的 79.3MB 我们算清楚了却没有动手清。最后一部分不是留尾巴,是想说清楚一个判断:盘点出数字和执行清理,是两件风险等级不同的事。
一、孤儿资产是怎么长出来的
3D 世界的资产生命周期和普通 Web 项目不一样。一个 GLB 模型上传之后,通常会经历:上传 → 摆进场景试试 → 调整缩放 → 换掉 → 删除场景引用。问题出在最后一步:删除的是"引用",不是"文件"。
场景配置里删掉一个模型是零成本的,而删磁盘文件是有风险的——万一别的地方还引用着它呢?于是绝大多数项目的默认行为是"引用删掉,文件留着"。日积月累,磁盘上会积累大量"没有任何场景引用它们"的文件,这就是孤儿资产。
还有一类更隐蔽:结构性的孤儿。glTF 系的资产是"JSON 元数据 + BIN 二进制缓冲"分离的结构,动画数据也在 BIN 里。如果某次修改是"删掉一段动画、只改了 JSON、没有重建 BIN",JSON 里不再引用的那段缓冲就成了孤儿字节——文件还在被正常加载,但里面有一部分数据永远没人读。这类孤儿不在磁盘目录层面,在文件内部,目录级的扫描抓不到它。
二、盘点怎么做:引用清单对磁盘清单
方法本身不复杂,两步:
- 建引用清单。 遍历所有场景配置、编辑器数据和资产注册表,把"被引用的资源"列出来。
- 建磁盘清单。 扫资产目录的实际文件,和引用清单做差集。
差集里有两种情况要分开处理:整文件无引用的(目录级孤儿),和文件内数据段无引用的(结构级孤儿,需要解析文件内部引用关系才能发现)。
做这件事真正的门槛不在代码量,在于敢不敢信差集结果。所以动手删除之前,还有一步:把"待删清单"先按类型、大小、最后引用时间分组过一遍,人工抽查。孤儿判定错了,删掉的可能是还在用的模型——这类事故没有便宜的后悔药。
三、盘出来的账
我们对自己资产库盘点的结果,两张表说清:
第一张:场景资产库——已清理。
| 项目 | 数字 |
|---|---|
| 孤儿文件 | 159 个 |
| 孤儿体积 | 2.9GB |
| 目录体积变化 | 3.1GB → 390MB |
这批是目录级孤儿:整文件无引用,按上面的流程人工抽查后删除。清理之后,这个资产目录从 3.1GB 降到 390MB——缩掉的部分是 92%,而世界里看得见的内容一个没少。
第二张:角色模板库——已量化,尚未清理。
| 项目 | 数字 |
|---|---|
| 模板库总体积 | 205.5MB |
| 其中的孤儿数据 | 79.3MB |
| 分布 | 集中在 4 个文件 |
| 最严重的一个文件 | 93.4% 的字节是孤儿 |
这批是前面说的结构级孤儿:成因是"删动画只改了 JSON、BIN 没有重建"。文件每次都跟着加载,但里面有 79.3MB 的缓冲数据没有任何动画轨道引用它。
四、为什么 79.3MB 先不动
算出来之后没有立刻删,三个原因:
一,清理方案和目录级删除不是一回事。 目录级孤儿是删文件,结构级孤儿要重建文件:把 JSON 里还在引用的缓冲段提取出来,重新拼一个 BIN,再校验动画轨道一条不少。方案我们已经定了,但这是一次有写风险的手术——重建过程中任何一步对不上引用,坏的是还在用的角色模板。
二,收益是预估,不是实测。 角色模板在跨世界场景里是必载资源,79.3MB 的孤儿数据跟着每一次跨世界加载走。清掉之后跨世界首屏预计能从几十秒降到 1~2 秒——注意这是预估,我们没有实测过清理后的首屏数字,因为清理本身还没有执行。写文章如果把这个预估写成已完成的事实,就和我们不编数字的原则冲突了。
三,缓存策略会放大操作风险。 我们线上模型资源走 30 天的强缓存,改了 GLB 内容、URL 没变的话,老访客一个月内拿到的还是旧文件。所以这类资产手术的最佳执行时机是和版本发布一起做,而不是随手做——这也是它被排进待办而不是立刻执行的原因。
一句话总结这笔账:3.1GB → 390MB 是做完的事,79.3MB 是算清楚、方案定了、还没做的事。 两件事我们都写出来,因为"盘点"这个动作的价值就在把账算明——执行可以排队,账不能糊涂。
五、你的项目要不要做一次资产盘点
| 这些信号出现了 | 说明值得盘 |
|---|---|
| 部署包或备份体积逐月上涨,但世界内容没变多 | 大概率有目录级孤儿 |
| 进世界加载越来越慢,逐项排查又找不到大文件 | 可能有结构级孤儿(文件内部) |
| 跨世界 / 跨场景加载牵着一堆"必载"资源 | 必载资源的内部垃圾会被放大到每次加载 |
| 团队里多人上传过资产、没有删除规范 | 一定有,早盘早干净 |
反过来说,两种情况不急着盘:项目刚起步、资产总量本身很小(盘的成本大于收益);或者你能确定所有资产删除都走了统一的清理流程(那孤儿本来就不该存在)。
六、边界与未实测项
- 79.3MB 的清理尚未执行,文中"跨世界首屏从几十秒降到 1~2 秒"是方案推算的预估收益,实测数字要等清理完成、跑了真实跨世界流程之后才有。
- 场景资产库的清理结果(159 个 / 2.9GB / 3.1GB→390MB)来自我们自己的验收记录,是已完成的事实。
- 盘点脚本的引用清单覆盖的是我们的场景配置、编辑器数据和资产注册表——如果你的项目还有别的引用源(比如硬编码在代码里的路径),要先补进清单再信差集结果。
- 删除环节我们人工抽查过;但"人工抽查"意味着它不是零风险操作,照做之前建议你对自己的待删清单做同样的事。
常见问题
Q:为什么不在上传和删除时就防住孤儿?
A:可以往这个方向做(删除引用时提示文件已无引用、定期自动生成待删报告),但要先解决"引用关系有且只有一个可信来源"这个前提。我们的引用来源不止一处(场景配置、编辑器数据、注册表),先做盘点把存量清掉,防增量的机制才有干净的基线。
Q:结构级孤儿为什么不常见到人提?
A:因为它需要解析文件内部结构才能发现,多数"磁盘占用分析"工具只看文件大小。这也是它最麻烦的地方:文件在被正常加载、功能完全正常,只有加载时间和流量在替它付账。
Q:390MB 之后还能再瘦吗?
A:资产目录这个量级已经比较干净了。我们还在做的是另一条线的瘦身:GLB 贴图压缩(−68%~−92%)、变体贴图剥离、LOD 三版本(118 组低模 ≤100 面)。那是"让留下的资产更小",和这篇的"把没人要的资产找出来"是互补的两件事。
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 虚拟世界基底——资产管线、性能治理、多端适配、AI 接入这些层已经铺好,资产卫生也只是它运维日常的一部分。官网(搜「创世虚拟世界CRM」即可找到)有一个可以走一圈的演示世界。
关于名字:本文说的创世Genesis,即创世虚拟世界CRM系统,两者是同一个自部署 3D 虚拟世界产品。若你通过「创世Genesis」没搜到我们,直接搜「创世虚拟世界CRM」即可。