中文  |  English

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

私有化部署一套浏览器 3D 空间,工程量到底在哪

私有化部署3D空间 自部署3D虚拟世界 Three.js私有化部署 去CDN化 内网3D空间 3D空间自研 GLB模型压缩 LOD分级 浏览器3D工程

摘要:很多方案书把"私有化部署"写成一句话——装个服务、配个域名就完事。但真在内网、离线、自有服务器上跑一套浏览器端 3D 空间,工程量集中在四个地方:依赖去 CDN 化、渲染内核升级、资产管线的自动化、以及加载与卡顿的可预算化。本文按这四块拆开讲,附上可复现的验收数字和一份"哪些场景该私有化、哪些别碰"的判断表。
私有化部署不是一个开关,是一整套"把外部依赖变成自己的责任"的迁移。这篇讲的是迁移过程中真正花时间的地方。

一、先定义清楚:"私有化"到底私有哪几样

选型时经常只谈一条——"数据在不在自己服务器上"。实际落地要过的关有四道:

私有化的含义最容易被低估的点
数据业务数据、资产、日志都在自有库/盘上备份与归档链路也要自建,不能依赖云服务
网络不访问公网 CDN,内网可跑依赖去 CDN 化——这是最容易漏的一步
运行时不依赖第三方 SaaS 的接口版本漂移:上游更新会静默改变你的行为
升级版本节奏由自己控制升级的回归成本要自己扛,所以验收必须脚本化

第二条和第四条是真正吃工作量的地方。下面按可验证的顺序拆。

二、去 CDN 化:最容易被当作"改几行 import"的一步

浏览器端 3D 项目起步时,几乎都会走 importmap 指 CDN 或者 <script src="unpkg...">。放到内网环境,这一层立刻全部失效;更麻烦的是双实例问题——本地引一份、CDN 引一份,同一个 THREE 被加载两次,运行时的 instanceof 判断会莫名其妙地失败。

我们最后走的路子是六步:

  1. esbuild 自建 UMD bundle,顶替掉原来的旧文件名(保持引用路径不变,减少改动面);
  2. 把加载器逐个本地化或 stub 化(用不到的直接给空实现,避免把体积拖进去);
  3. 加一层 shim 垫片(我们这一版是 87 个符号),把历史 API 接到新内核上;
  4. 后处理库(postprocessing)本地 vendor
  5. importmap 从 CDN 改指本地——这一步才是消除双实例红线的地方
  6. 加兼容层,把旧的输出编码/贴图编码 API 真映射到新的颜色空间 API 上,而不是简单删掉。

配套一条很实用的纪律:把遗留 API 清单化。我们扫出 79 处只用一个兼容层兜住,而不是去逐个改写业务代码——私有化项目里,业务代码改动的每一行都是回归风险。

降级也要自己管。公网上可以假设浏览器都支持 WebGL2,内网终端不一定(老机器、远程桌面、虚拟机显卡)。做法是在加载 three 之前做能力检测,不支持就直接全屏中文提示 + window.stop():宁可给出明确结论,也不要白屏 + 控制台刷几百行报错。

三、渲染内核升级:为什么"版本号"这件事值得单独干一轮

从 r128 到 r185 跨度很大,中间横着 r152 的颜色管理革命。旧基线其实本身就是错的:sRGB 输出配上按线性着色的贴图 = 双重 gamma,画面偏亮偏粉。很多人以为这是"美术给的贴图颜色有问题",其实是管线的问题。

升级收益不在数字上,在能力面上:

  • 颜色正确性:管线对了,颜色才准,这是"看得见的画质升级";
  • WebGL2 能力面:自研的高斯泼溅 shader、BVH 碰撞、实例化合批,全都吃 WebGL2 的红利;
  • 可离线:去掉 CDN 之后,内网和断网环境都能跑。

怎么证明升级没把画面改坏——这是私有化项目必须回答的问题。我们的做法是统一口径的基线截图 + 自研的 PSNR / 差异像素工具:后台页 43.4dB 一致,主世界的差异全部能归因到"颜色被修正了",构图、建筑、角色是像素级一致的。"差异可归因"比"差异很小"更重要,否则你没法判断哪一处是 bug。

四、资产必须在自己的服务器上"自我消化"

私有化之后,带宽是你自己的,硬盘是你自己的,用户上传的模型也是你自己要兜住的。所以资产管线不能靠人工,必须上传即自动处理

4.1 模型瘦身:不改几何的手术

核心思路是"用 JSON + BIN 手术改写 GLB,不动几何",按用途分级压缩:法线/遮蔽类做无损重编码,baseColor 走调色板量化。实测:

样本处理前处理后降幅
课桌模型25.74 MB8.22 MB−68.1%
4K 多 buffer 模型20.7 MB4.38 MB−92.5%

这类二进制改写最容易在生产环境翻车,所以我们设了五重闸门:mesh 名匹配、压缩格式校验、多 buffer 布局校验、几何一致性校验、回读验证。任何一关不过就跳过不落盘——私有化环境里没有"线上发现问题再回滚"的余地。

4.2 LOD:让"远景成本近零"变成可承诺的规格

LOD 的常见做法是按锚点距离分带,我们的做法是按玩家到模型表面的距离分带。差别在哪:一个横跨 155 米的模型,锚点可能在很远处,按锚点距分带会把它误判成"远景"而换成低模,玩家走到边上还是看见一个糊模型。用表面距就不会。

低模由工具链生成,超出拓扑下限的部分用自研的贪心边坍缩兜底。结果是可以对外承诺的硬指标:全库 118 组低模,118/118 ≤ 100 面

三个机位开/关 LOD 的三角数对比:

机位关闭 LOD开启 LOD降幅
教室热点−85.8%
出生点−71.2%
重复实例密集的族−86.7%

还有一个细节值得抄:变体不额外占显存。低模变体和高模共享同一个贴图对象(不是复制一份),我们做了断言校验(33/33、36/36 命中共享)。另外变体的贴图会被自动剥离,磁盘省下 1.3GB;离线部署包里干脆不含变体,首次访问时现场生成(118 条 36.7 秒)。

五、把"卡"变成可预算的资源

自有服务器上,加载体验只能自己扛。我们把它拆成四个可以单独计量的问题:

① 解析阻塞主线程。 GLB 解析移进 Worker,用可转移对象做序列化,纹理走 OffscreenCanvas 预降。验收 23/23 走 Worker 且渲染完整。

② 驱动层的着色器编译尖峰。 这是最反直觉的一处:在 D3D11 上,同步的着色器反射一次要 160~340ms,且不可中断。灯的数量一变,整个场景要重编译,画面直接冻住。我们的做法是点光灯池——固定 12 盏常驻、租用归还,灯数变化不再触发全场景重编译;配合"预热预算",未预热的 mesh 先不入屏,按 170ms/帧逐个把反射成本摊薄,把"画面死住"换成"模型稍晚出现"。

效果:灯数变化造成的冻结从 8989ms 尖峰 → 0;密集区 draw call 3064 → 837(−73%)

③ 远景一片空白。 远处直接变成占位方块,观感很差。做法是一张全图占位场:1 个 draw call 覆盖 1000+ 对象,250ms 渐缩退场。实测 1071/1071 覆盖,draw call 2150 → 956(−56%),实现了"远看有东西、近看是真模型"。

④ 加载进度必须是真的。 进度条的分母取当前视野的兴趣集,权重是 0.7 下载字节 + 0.3 确认上屏,并且对迟到的加载任务做代际隔离(否则旧请求回来会把进度条往回拽)。私有化环境下这条尤其重要:没有 CDN 加速,用户等待时间更长,进度反馈就是那条"我没死"信号。

六、私有化之后,运维就是你的 KPI

这一块没有技术含量,但决定了这套东西能不能"活下去"。

验收脚本化、可重跑。我们仓库里有 54 个 accept_*.js,判据是真实退出码 + JSON 报告 + VERDICT ACCEPTED。LOD 23/23、AI 接入 75/75、高斯泼溅 26/26、骨骼回归 67/67——每次改动都能回归。私有化项目没有"灰度发布"这种缓冲手段,验收脚本就是你的安全网。

配置热调。后台改参数(分带距离、推送档位、速度上限、并发上限)应该 60 秒内生效,不要让客户为改一个阈值去发版。

约束换可维护性。我们给自己定了硬约束:单文件 ≤500 行(超 1000 行禁止追加)、大文件黑名单零追加、新功能必须独立模块、临时脚本用完就删。听着像洁癖,但这是长周期项目不烂尾的制度保障。

七、说清楚现在的短板(不藏)

私有化部署最忌讳"方案书写得比代码好"。如实列我们当前的两处:

  1. 角色的帧率解耦没做完player.js 目前仍是"每帧固定增量",低帧率设备上移动和转向会变慢(不只是画面卡,是手感变慢)。这是当前代码里一个真实短板,不是待优化项。
  2. 角色模板库有 79.3MB 孤儿数据。模板库总计 205.5MB,其中 79.3MB 是历史遗留(删了动画只改 JSON、BIN 没重建,集中在 4 个文件,最严重的一个文件 93.4% 是垃圾)。清理方案已定,跨世界首屏可从几十秒降到 1~2 秒,但还没实施

把这两条写进对外材料,比写十条优点更能建立信任——因为它说明你真的在维护这套东西。

八、什么场景该走私有化,什么场景别走

场景特征判断
内容涉密、内网封闭、不能出网必须私有化,CDN 方案在这里直接失效
已经有 Three.js 团队、要深度定制走自部署基底,别买模板——模板的边界会成为团队的天花板
数据合规要求明确(数据不出境/不出内网)走私有化,且要连日志归档链路一起自建
只是验证一个想法、两三个月要上线别私有化。SaaS 平台更快,先验证需求
团队里没有人碰代码、也没预算找人部署别私有化。这套东西是基底不是成品,启动成本会超预期
产品本身不需要空间呈现别做 3D。这是最容易浪费的钱

九、常见问题

问:私有化部署一套浏览器 3D 空间,最难的是哪一块?

答:不是部署本身(现在容器化其实不难),难的是去 CDN 化 + 资产管线自动化这两块。前者关系"能不能在内网跑起来",后者关系"用户传上来的东西会不会把你的服务器拖垮"。

问:不用 CDN,加载速度会不会差很多?

答:会差,所以要靠工程手段补:模型瘦身(−68%~−92%)、LOD 三版本、Worker 解析、占位场 + 预热预算。注意 brotli/gzip 压缩传输是标配,压缩后实际传输量通常只有源文件的四分之一左右。

问:内网机器显卡差,支持不了 WebGL2 怎么办?

答:做加载前的能力检测 + 明确的中文提示,而不是白屏。如果终端确实老旧,私有化项目里要把它当作一个需求去评估,而不是上线后发现。

问:私有化之后升级怎么办?

答:靠可重跑的验收脚本。我们 54 个脚本、每个功能都有明确判据,升级前跑一轮就知道有没有回归。

问:数据在自己服务器上,还需要额外做什么?

答:备份与归档链路要一起自建(聊天记录、审计日志、上传资产),并且要明确归档失败时的处理策略——我们的做法是"未确认上传成功前不删本地文件",宁可占盘也不丢数据。

问:这套东西能二次开发吗?

答:私有化部署的前提就是底层代码在你自己手里。要注意的是留好扩展边界:新功能独立模块、不往大文件里追加,否则二次开发会变成考古。

方案对比:私有化部署 vs 平台租赁

对比维度平台模式(SaaS)私有化部署(创世Genesis)
数据主权数据存储在平台服务器,归属模糊数据在你的服务器,完全自主
成本模式按月/年付费,长期成本累计高昂一次性部署投入,长期成本极低
功能定制标准模板,功能固定不可改完全自由定制,按需扩展
品牌独立受平台品牌和调性限制独立品牌形象,完全自主设计
用户归属用户属于平台,你只是租客用户是你的,数据是你的,关系是你的

关于创世Genesis

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

如果你正在评估自部署 3D 空间这条路,可以参考我们的实现:创世Genesis 是一套浏览器端、可部署在自有服务器上的 Three.js 3D 虚拟世界基底。上面写的指标都来自仓库里可重跑的验收脚本,欢迎直接复跑核验。

本文为开发过程中整理的技术复盘,数据来自项目内的验收脚本,可直接复跑核验。文中标注为「当前短板」的部分属实未完成,不作为已交付能力。

源码与仓库

三个地址内容一致,国内访问用上面两个更快。仓库里有部署说明与演示入口。

← 返回文章列表