中文  |  English

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

一套 3D 前端怎么同时撑住手机、平板和 XR:按能力分档,不按设备分叉

3D 多端适配、浏览器 3D 移动端性能、手机 3D 卡顿、draw call 优化、LOD 资产分级、着色器预热、灯池、WebGL2 兼容、弱网重连、多人在线带宽预算、3D 内存泄漏

做 3D 项目时,"支持手机"这句话通常是最后才提的——它躺在需求文档角落里,等你交完桌面版再想起来。真正开始做才发现,为手机重写一套不是选项:桌面端独有的东西(鼠标右键菜单、悬浮提示、滚轮缩放、键盘快捷键、拖拽文件)在触屏上一个都不成立;而手机独有的问题(内存上限、GPU 差异、横竖屏切换、切后台被冻结)在桌面端根本不会遇见。

这篇讲我们怎么处理这件事:一个世界、四个终端、能力分档而不是代码分叉。也讲清楚哪些已经做完了、哪些只能算"结构上留了位置但还没实测"——后者我会在文中明确标出来,不含糊过去。

一、为什么"响应式"这个词在 3D 里基本没用

网页上的响应式是布局问题:宽度变了,flex 换行、字号缩放、图片换分辨率。3D 不一样,渲染开销不是由布局决定的,是由场景规模决定的。

同一份代码,在一台开着十几个标签页的办公电脑上和一个四年前的手机上,画质差得不是一点半点。原因不是"屏幕小",而是:GPU 算力差一个量级、可用内存差一个量级、浏览器能给的 WebGL 上下文更受限。

所以第一件要定下来的事不是"怎么适配手机",而是:你的项目里,哪些东西必须跟着设备能力走,哪些东西必须保持一致。

我们把这条线画成四档,而不是四种设备:

档位谁进得来帧率预算的取向资产策略
高桌面独显 / 高端移动保画质全量资产,全画质
中主流桌面、集显、现代手机保流畅LOD 生效,阴影降级
低老旧设备、远程桌面、加载期保能进占位场先撑住,模型逐个补齐
XR头显,双目渲染、必须 72Hz 以上保稳定帧最激进的几何与特效预算

关键是:档位是运行时算出来的,不是设备类型写死的。 同一台手机在办公室 Wi-Fi 下和地铁里的判断可以不同。常见做法是组合几个信号:GPU 渲染器字符串、屏幕物理像素密度、devicePixelRatio 上限、以及一段短帧的现场采样——先按保守档起,跑完再看实际帧率往上调。

二、四个终端各自的坑,和对应的机制

手机:内存比帧率更容易先崩

手机上做 3D,帧率掉到 25 fps 用户还能忍,内存爆掉是直接闪退。 而内存泄漏在开发机上不会暴露——你刷新几次就干净了,浏览器的 GC 会替你擦。

这类问题的隐蔽之处在于它有三种形态:真正的泄漏(对象还被引用着,GC 收不掉)、无界增长(日志、聊天记录、事件缓冲一直追加)、以及卸载不干净(离开场景了,纹理和几何还挂在 GPU 上)。第三种最常见也最隐蔽,因为它只在反复进出场景之后才显形。

机制上的做法是全链路回收:视频 DOM、灯池、气泡、僵尸连接逐项释放,纹理与几何在场景切换时统一销毁。顺带说一句:这类问题只能用工具查,肉眼看不出来——我们在一次内存审计里修了四项泄漏,靠的是逐项对比进出场景前后的占用。

第二个手机专属问题是触摸与手势。桌面端鼠标能做的事,触屏上要么没有对应手势,要么会和浏览器的默认行为打架(页面缩放、滚动、后退)。所以移动端要显式声明视口、拦截不该抢的手势、把"点"和"拖"区分开——这块每个项目的交互不一样,属于你要自己写的第 5 层。

平板:最被低估的一档

平板尴尬在于它既不是手机也不是电脑:屏幕大、距离远、GPU 接近手机、输入是"触摸但能悬停"(如果配了触控笔)。

实际上它的处理方式和手机是一套的,值得单独提的是它的首屏问题更明显——平板常在"随手点开"的场景里被打开,进来第一眼就卡住的代价,比手机还高(因为屏幕占比更大、用户预期更接近电脑)。

桌面:别把"能跑"当成"够好"

桌面端的问题不是兼容性,是你的开发机太好。你在自己机器上测 60 fps,换一台集显笔记本可能只有 25 fps,而用户不会认为这是设备问题,会认为是你的东西做得糙。

治法是把渲染开销归因做成诊断能力,而不是靠感觉:逐对象计数(onBeforeRender)、转视角卡顿归因(diagTurnLag)、全场扫描(diagTurnSweep)。这些接口的实际用途是——在用户投诉卡顿之前,你先知道是什么在卡。

XR:帧率不达标就不能用的那一档终端

前三个终端掉帧是体验变差。XR 掉帧是眩晕和呕吐,因为双目渲染对运动连续性的要求高一个量级。

XR 有几条不能协商的:帧率不能动态降(掉下去比低更糟)、立体渲染的开销是单眼的两倍、手柄输入和手指输入是两套完全不同的交互。

所以 XR 在我们的分档里是独立一档,而不是"桌面里的高档位"。这不是把 XR 做好了顺带能跑,而是另一套预算和另一套交互。 我们的 XR 适配是结构性的(输入抽象与分档框架都预留了位置),但要说明白:XR 场景下的实机帧率表现我们还没有做完整的实测,这篇不给 XR 的帧率数字。

三、让同一份代码撑住四个终端的几件事

这一节是上面那些坑对应的具体做法,都是已经在跑的:

资产分档是收益最大的一件。 远处模型降面(三档 LOD,118 组低模 ≤100 面、三角数 −71%~−87%)、贴图按用途分级处理(−68%~−92%)、变体的贴图剥离(215 件 0 失败)。三件事做下来,同一个世界在弱设备上能跑的、在强设备上也能跑。

draw call 治理决定规模上限。 几何合批后密集区 draw call 3064 → 837(−73%),加载期占位场 2150 → 956(−56%)。占位场那一项对弱设备特别重要:一个 draw call 覆盖 1071/1071 个对象,模型就位后 250 毫秒渐缩退场——用户永远先看到完整的世界,而不是一堆散件。

着色器编译的预算要提前算。 未预热的网格不入屏,编译后按每帧 170 毫秒的份额结算。实测出生点首进最长 386 毫秒,超过 500 毫秒的长帧为 0。这件事在桌面端是优化,在弱设备上直接决定"进去第一眼卡不卡"。

灯的编译成本在弱设备上放大得最明显。 场景里点光源数量一变,所有材质重编译。实测一次灯数变化触发 8989 毫秒的全场冻结。灯池(12 盏常驻、租用归还)让这个成本归零。

加载期不能白屏。 加载期白屏是体验成本里最直接的一条——在弱设备上白屏时间会被拉长好几倍。我们用全图占位场加渐缩退场来处理。

带宽也要分档。 弱设备往往同时处于弱网。首屏实测 838 KB、99 个请求、1.7 秒,是资产分档之后的量级;不做分档的世界,进世界十几秒还在拉资源。

不支持的能力要说人话。 WebGL2 不支持时给一句明确的提示,而不是白屏或控制台刷错——这条在弱设备上命中率比想象中高。

四、切后台、锁屏、断网:多端特有的三种死法

手机和平板会被系统暂停:切后台、锁屏、来电。被暂停的时候 WebSocket 可能被回收,回来时连接已经断了但你不知道。

  • 重连不等于刷新页面。 我们的做法是把在场状态缓存成消息,每次重连后自动重发,加无限重连和应用层 PING/PONG 看门狗。实测 9/9 场景下重连后对端仍然能看到我移动。
  • 服务器要知道连接已经没了。 有告警、有未登记连接的检测,否则你看到的在线人数是虚的。
  • 语音这类资源要有名额和回收。 近距离语音用半双工 + 同时说话人数名额制,实测 10 人约 1.3 Mbps、20 人约 2.6 Mbps,可以直接进预算。

这三件事在桌面端很少同时发生,所以在桌面端测不出来——它们只在真实的多端使用里出现。

五、这套方法复用到别的终端上

上面这套"按能力分档"的做法,价值不在于我们支持了几个终端,而在于它可以被你拿去做下一个终端:

新终端要回答的问题分档框架里的位置
它的 GPU 能力在第几档?运行时探测逻辑,不用改
它需要的帧率底线是多少?决定它属于独立档还是某档的收紧版
它的输入方式是什么?输入抽象层,新增一种映射,不碰渲染
它有独特的系统事件吗?(切后台、来电、省电模式)在生命周期的回收点上挂钩子

换句话说:终端不是一份配置,是一个"能力档 + 输入映射 + 生命周期事件"的组合。 每加一个终端,改的是这三处,不是整套渲染。

六、这套方法的边界

必须说清楚的几条:

  • 移动端与 XR 的实机表现我们没有完整实测。 文章里给的数字全部来自桌面端的验收脚本(脚本在仓库里,可以自己跑)。移动端和 XR 的具体帧率、耗电、发热,需要你在自己的目标设备上实测——不要拿我们桌面端的数字去推断手机表现。
  • 档位探测没有万能阈值。 GPU 字符串、像素密度、短帧采样这些信号该怎么加权,取决于你的场景和目标用户群。我们给的是机制,不是配方。
  • 手势和 UI 是第 5 层。 上面四档管的是渲染预算和资源回收,但"点一下放大还是双指放大"这种交互,每个产品不一样,那部分永远是你自己写。
  • 弱设备上的取舍是产品决策,不是技术决策。 一个世界在老旧手机上能不能进,取决于你愿不愿意砍功能。技术上能跑,产品上砍不砍是另一回事。
  • 我们目前自己有一个已知短板:移动操作仍是"每帧固定增量"的推进方式,低帧率下手感会偏慢。这条写在我们的技术说明里,没有藏。

常见问题

Q:为什么不干脆分三个工程?

A:可以,但代价是三倍维护成本和三次分叉风险。真实的问题是"哪些东西要跟着设备能力走"——把它们抽成分档,桌面端和移动端就能共用一套代码,真正分叉的只剩下输入映射那一小层。

Q:多端适配的工作量大概占多少?

A:我没有可靠的工时数字,也不编一个。可以给你的是判断方法:把上面第二节的坑逐条对着你的场景过一遍,列出"你必须自己写"的那部分,剩下的就是基座已经覆盖的。

Q:手机端帧率能到多少?

A:给不了数字,因为手机端我们还没做完整实测。桌面端的实测值可以引用(首屏 838 KB / 99 请求 / 1.7 秒),手机端建议你在目标机型上自己跑。资产分档那一套做不做,对手机的影响比换任何一个框架都大。

Q:XR 需要额外的工作吗?

A:需要,而且不是调参数级别的。双目渲染的开销、手柄与手指两套输入、不能动态降帧的要求,都让 XR 更像一个独立档位而不是高配桌面。我们目前是结构上留了位置,实机表现待测。

Q:占位场是"假内容",会不会被用户当成偷工减料?

A:取决于退场做得顺不顺。做法是一个 draw call 覆盖全部对象(实测 1071/1071),模型就位后 250 毫秒渐缩退场——用户先看到一个完整的世界,再逐步换上细节。做好了反而是加分项,因为"进世界有东西"比"进世界空白"体验好。

源码与仓库

三个地址内容一致,国内访问用前两个更快。仓库里有部署说明和一批验收脚本,可以自己跑一遍看桌面端的实测值。

  • 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」即可。
← 返回文章列表