做 AR 这些年被问得最多的一句是“这玩意儿到底什么时候能真正用起来”问这话的人里有做展厅设计的有做设备巡检的也有刚接了个“AR 导览”需求、完全不知道从哪儿下手的产品经理。大家的困惑其实很一致增强现实AR这个词听了快十年手机里也能扫出个虚拟沙发摆在客厅可真要自己做一个能交付的东西就发现处处是坑——模型飘、光照假、手机烫、用户戴三秒就头晕。这篇东西我想把 AR 从概念到落地完整拆一遍它到底解决什么问题、底层靠什么撑起来、硬件怎么选、代码怎么写、坑在哪儿。不管你是刚接触这个方向的新手还是已经做过一两个 Demo、正卡在“Demo 很酷但没法交付”这个阶段的从业者下面这些内容应该都能直接拿去用。我会尽量少讲空话多讲那些文档里不写、但你在现场一定会撞上的细节。1. 先把 AR 这件事说清楚它到底解决什么问题AR 的本质不是“在屏幕上放个 3D 模型”而是把虚拟信息锚定在真实空间中并让用户相信它就在那儿。这句话里有两个关键词锚定、相信。锚定是技术问题——坐标系对齐、位姿追踪、误差收敛相信是体验问题——光照、阴影、遮挡、材质、运动一致性。绝大多数失败的 AR 项目都是只做了第一个然后在第二个上翻车。理解这一点非常关键因为它决定了你的技术选型和验收标准。一个“能扫出模型”的 Demo验收标准是“模型出现了”而一个能交付的 AR 应用验收标准是“用户在移动手机时模型没有跟着漂移脚底阴影方向和环境光一致手从模型前面划过时模型被正确遮挡”。这两者的工程量差着十倍。1.1 三条技术路线标记式、平面式、空间式从追踪原理上分AR 大致走过三代而且这三代今天仍然并存各有各的适用场景不是新的就一定好。第一代是标记式Marker-based。系统识别一张特定的图案比如一张印着黑白方块的卡片根据图案在画面中的四个角点算出相机的位姿然后把内容贴在图案上。它的优点是极其可靠、计算量小、几乎任何带摄像头的设备都能跑缺点是必须看见标记看不见就没了而且标记露出来很破坏观感。第二代是平面式Surface/Plane-based也就是现在手机 AR 的主流形态。系统一边跑视觉惯性里程计估计相机运动一边从画面里提取平面用户点一下屏幕系统就在那个平面上打一个锚点。它不需要标记缺点是初始化和重定位需要一点时间光线暗、纹理少、纯白墙面前容易失败。第三代是空间式Spatial/Map-based代表形态是头戴设备上的大空间理解不只是找平面而是重建整个房间的网格、识别墙/地/天花板、记忆空间结构、支持多人共享同一套坐标系。这一代对算力和传感器的要求最高通常要配合深度传感器或双目。选择逻辑很直白如果你的场景是固定展台、说明书、卡片互动标记式最省事且最可靠如果是自由摆放、导览、试摆家具平面式是默认答案如果要多人协作、跨设备共享、长时间佩戴才需要考虑空间式。我见过不少团队一上来就奔着空间式去结果在一个只需要“扫一扫出模型”的展项上烧了三个月。1.2 和 VR、MR 的边界到底在哪儿这三个词经常被混用实操中搞混会直接导致需求错配。我的划分标准只有一条虚拟内容与真实环境的耦合程度。VR 是完全隔绝用户的视觉输入 100% 由渲染生成所以它可以把场景做得无限大、无限绚但用户对现实世界的感知被切断。AR 是把虚拟内容叠加到现实之上但系统未必理解现实——早期那种把模型贴在摄像头画面上的“伪 AR”就属于这一类模型和现实只是图层关系没有空间关系。MR 则要求系统真正理解现实几何虚拟物体能被真实物体遮挡、能被真实光源照亮、能发生物理交互。落到工程上这个区别意味着做 VR你的性能预算几乎全给渲染做 AR你要分一大块算力给感知追踪、平面检测、语义分割做 MR你还要额外做空间网格和遮挡处理。同样的硬件能跑的 VR 场景复杂度可能是 AR 的三到五倍这个账一定要提前算。1.3 一条内容从制作到落地要经过哪些环节很多人以为 AR 项目的成本大头是写代码其实不是。一个完整的 AR 交付链路大概是这样内容准备三维建模、减面、UV、烘焙、材质转换、动画导出格式与压缩转成运行时友好的格式glTF/GLB 是通用选择做纹理压缩ASTC/ETC2控制单文件体积场景搭建标注锚点位置、配置触发逻辑、设置缩放与朝向追踪与注册平面检测或标记识别、位姿求解、坐标系对齐渲染与光照环境光估计、阴影投射、PBR 材质校准、遮挡处理交互层手势、射线拾取、UI 面板、音频反馈发布与运维包体分发、内容热更新、埋点、异常回收这里面建模和素材规范往往是最容易被低估的一环。一个在 DCC 软件里看着很精致的模型导出之后发现面数二十万、贴图四张 4K、单文件八十兆手机上加载要等十秒——这个项目基本就废了。我在实际项目里定的红线是单个交互模型面数控制在 5 万面以内贴图合并成一张 2K 图集单文件压缩后不超过 8MB。这个数字不是玄学是按“4G 网络下三秒内加载完”倒推出来的。2. 技术底座拆解AR 系统的四层结构不管用什么框架AR 系统在逻辑上都可以切成四层感知层、注册层、渲染层、交互层。分层的好处是排查问题时你能快速定位是哪一层出了毛病。模型飘是感知层模型位置对但看着假是渲染层点不中是交互层。2.1 感知层SLAM 和 VIO 到底在算什么这是整个 AR 最硬核的部分也是自研门槛最高的部分。核心问题是设备在一个自己没去过的空间里移动怎么实时知道自己在哪儿、朝哪儿看。视觉 SLAM 的基本流程是从每帧图像里提取特征点角点、斑点计算描述子跟上一帧或历史关键帧做匹配得到一组 2D-2D 的对应关系用对极几何解出相对运动再三角化出三维点最后用光束法平差Bundle Adjustment联合优化相机位姿和地图点。数学上就是最小化重投影误差$$ \min_{T, X} \sum_{i,j} \rho\left(\left| u_{ij} - \pi(K , T_i , X_j) \right|^2\right) $$其中 $u_{ij}$ 是观测到的像素坐标$K$ 是相机内参$T_i$ 是第 $i$ 帧的相机位姿$X_j$ 是第 $j$ 个三维点$\pi$ 是透视投影$\rho$ 是鲁棒核函数用来压制误匹配。纯视觉方案有个致命弱点尺度不可观。单目相机看到一个物体在动分不清是物体小距离近还是物体大距离远。解决办法就是引入 IMU做视觉惯性里程计VIO。IMU 能提供高频的加速度和角速度虽然积分会漂但短时间内的尺度信息是可靠的两者互补。工程上通常用紧耦合方案把状态向量设成 15 维状态量维度说明位置 p3世界系下的平移速度 v3世界系下的线速度姿态 q4四元数表示的旋转加速度计偏置 ba3随温度和使用漂移必须在线估计陀螺仪偏置 bg3同上这里有个实操中极容易被忽略的点IMU 的偏置标定。不同批次的传感器零偏能差出好几倍。如果启动时不重新标定初始化的那几秒钟里追踪会明显发飘。我的做法是在应用启动时做一个两秒的静止检测采一段静止数据估计初始零偏代价很小但收益很明显。另一个参数是初始化时的激励要求。VIO 要恢复尺度需要设备在启动阶段有足够的平移和旋转激励。如果用户一打开就把手机举着不动初始化可能卡住十几秒。所以多数应用会在 UI 上引导“请缓慢移动手机扫视周围”这不是多余的设计是硬需求。2.2 注册层从世界坐标到屏幕像素的变换链注册Registration解决的是“虚拟物体的坐标怎么跟真实空间对上”。这条变换链上任何一环出错表现都是“模型位置不对”。完整的链路是世界坐标系 → 锚点坐标系 → 物体局部坐标系 → 相机视图 → 裁剪 → 屏幕。写成矩阵就是$$ p_{clip} P \cdot V \cdot M_{anchor} \cdot M_{obj} \cdot v_{local} $$$M_{obj}$物体在锚点下的局部变换你自己设的位置、旋转、缩放$M_{anchor}$锚点在世界系下的位姿由追踪系统给出$V$视图矩阵相机位姿的逆$P$投影矩阵由内参和近远裁剪面决定排查位置问题时我一般按这个顺序逐环验证先打印锚点位姿看它是不是在合理范围内比如平面锚点的高度应该在相机下方 1 到 1.5 米再看物体局部变换的缩放是不是被设成了 0.01 或 100 这种量级错位的值最后看投影矩阵的 near/far 是不是设得太极端导致深度精度丢失。我遇到过最离谱的一次是美术同学在导出时把单位从米改成了厘米结果模型看起来“飘在天上”其实是放大了 100 倍。关于坐标系约定也要特别注意渲染引擎普遍用右手系、Y 轴向上而视觉追踪系统的世界系可能是 Y 轴向上也可能是 Z 轴向上还可能带重力对齐的不同约定。跨框架对接时这个转换写错一次模型就会躺倒或者朝里。建议在初始化阶段放一个已知朝向的测试立方体肉眼确认朝向正确之后再接正式内容。2.3 渲染层为什么 AR 的“真实感”卡在光照上虚拟物体看着假八成不是模型做得不好而是光照不匹配。人眼对光影的敏感度远超对几何精度的敏感度——一个光照正确但只有五千面的模型比一个光照错误但五十万面的模型看起来真实得多。移动端 AR 的光照估计通常来自两个方向。一是环境光估计系统从相机画面里采样估算出整体色温和亮度输出一组球谐系数Spherical Harmonics你把它喂给材质的环境光项模型的高光和暗部就会带上环境的色偏。二是主光方向估计系统给出一个最主要光源的方向和强度用来生成方向性阴影。球谐系数一般用二阶的 9 个系数就够其中 3 个是 RGB 的直流分量也就是整体亮度色偏剩下 6 个描述方向性变化。工程上的做法是把这 9 个数直接写入着色器的环境光探针注意不要把强度直接乘以 1要先归一化否则在强光环境下模型会亮到发白。我一般的做法是给环境光强加一个上限钳制比如不超过 1.5 倍基准值。阴影是另一个大坑。很多项目直接用一个圆形贴图当假阴影环境光一变就穿帮。稍微认真一点的做法是用方向光做实时阴影贴图配合一个简单的接收面。但这里要注意阴影接收面通常是检测到的平面和真实地面的高度可能存在几厘米的偏差如果阴影贴图的分辨率不够就会出现阴影浮在桌面上方或者陷进去的效果。我通常会把阴影接收面稍微下压 2 到 3 毫米视觉上反而更贴合。2.4 交互层手势、射线和锚点的组合拳AR 的交互设计和平面 UI 完全不同因为用户的手和屏幕是分离的屏幕上的“点击”对应的真实意图需要重新设计。最可靠的是射线拾取从屏幕触点出发向场景里打一条射线和虚拟物体求交。配合“点一下平面 → 打锚点 → 物体落在锚点上”这套流程是移动 AR 里最稳的交互范式。它的好处是容错高用户即使偏了几十像素也能选中。手势方面单指拖拽移动、双指旋转缩放是通用约定但要注意拖拽的映射方式屏幕位移直接映射到世界位移在不同距离上感受完全不同——物体离得远时同样的屏幕位移对应的世界位移可能是近处的几十倍。常见解法是按照物体到相机的距离做比例缩放或者干脆把拖拽限制在锚点所在平面的二维移动上只允许在平面内滑动这样手感最接近真实搬东西。还有个容易忽略的点是反馈。AR 里用户没有触觉反馈全靠视觉和听觉。放置成功时给一个轻微的音效加一个缩放弹出动画能显著提升“东西真的放在那儿了”的心理确认感。这个细节在 Demo 里看不出来但在真实用户测试里差别很明显。3. 显示与硬件方案怎么选技术方案定完下一个决策就是跑在什么设备上。这个选择基本决定了后面所有的工程约束一旦定错返工代价极大。3.1 手持式、头戴式、车载式的取舍形态典型视场角算力约束佩戴/使用成本适合场景手持式手机/平板等价于相机 FOV约 60°–70°中受发热限制明显极低自带设备展示、导购、导览、试摆、轻量培训头戴式一体机40°–55°高需专门的散热与功耗设计中高需佩戴调整工业巡检、远程协作、沉浸培训车载式HUD8°–15°低功能单一极低被动使用行车信息投射桌面式空间显示30°–40°高高需固定安装展馆固定展项选型的核心判断不是“哪个先进”而是用户的手是不是空着的、使用时长是多久。手持式最大的问题是长时间举着累所以适合“几十秒一次”的轻交互头戴式的优势是双手解放适合“要一边看指引一边操作设备”的重交互场景代价是佩戴舒适度和续航。我在一个设备检修的场景里做过对比测试同一套内容手机版用户平均单次使用 90 秒就放下休息头戴版可以连续用 20 分钟以上。这个差异直接决定了方案该怎么选跟技术本身先进与否无关。3.2 光学方案的横评头戴设备的光学是成本大头也是体验差异最大的地方。主流方案有四种**Birdbath曲面半反半透**的结构是显示器发出光线经过一个半反半透的曲面镜反射到人眼同时外界光线穿过这个曲面镜进入人眼。优点是成像质量好、色彩和对比度容易做、成本相对低缺点是光效率低外界光被半反半透膜衰减导致透视亮度不足而且整个模组比较厚重。几何阵列光波导是在玻璃基板里做一系列半反半透的斜面光线在基板内多次反射后被这些斜面逐次耦出。优点是透视效果好、镜片薄、外观接近普通眼镜缺点是工艺复杂、良率低、每一片的显示一致性难控制而且容易出现“鬼影”多个耦出面的重影。衍射光波导用纳米级的光栅结构实现耦入耦出。优点是设计自由度高、可以做更大的眼动框缺点是有明显的色散彩虹效应而且衍射效率随波长变化导致画面偏色需要额外的补偿设计。自由曲面棱镜是一块厚实的自由曲面光学件体积大但成像质量最容易做早期产品大量采用现在主要用在成本敏感或对体积不敏感的场景。简单总结一句选型口诀要画质和成本选 Birdbath要轻薄和外观选光波导要便宜大碗选自由曲面。没有全能方案只有匹配场景的方案。3.3 关键参数怎么算这部分很多人是靠供应商给数据自己不算结果方案定下来才发现不够用。几个核心参数其实都能自己估。**PPD每度像素数**是衡量清晰度的关键指标比总分辨率有用得多$$ PPD \frac{\text{单眼水平像素数}}{\text{水平视场角}} $$举例单眼分辨率 1920×1080水平 FOV 50°则 PPD 1920 / 50 38.4。人眼中心凹的视锐度约 60 PPD所以 38.4 PPD 意味着你能看出像素感文字边缘会发虚。要做到“看不出像素”得往 55 到 60 PPD 走这是为什么高 PPD 方案的分辨率要求那么吓人。入眼亮度的账也要算。设光波导的耦出效率为 1%环境光透射率为 20%那么要让用户看到的虚拟画面达到 500 nits光源端至少要输出 50000 nits。这就是为什么这类设备的显示模组要求极其苛刻也是功耗和发热压力的来源。户外场景需要的亮度可能是室内的三到五倍这个差距在选型时如果没考虑产品到户外就完全不可用。**眼动框Eyebox**指的是眼睛在多大范围内移动仍能看到完整画面。常见值在 6 到 12 毫米之间。眼动框越小佩戴位置要求越苛刻用户稍微动一下就出画。这也是为什么大眼动框是光波导方案的核心竞争力之一——它直接决定了“能不能给不特定用户戴”。4. 动手做一个最小可用 AR Demo理论讲完来点能跑的东西。这一段我用浏览器原生的 WebXR 接口写一个最小可用的平面放置 Demo。选它的理由是不需要安装任何 SDK不需要打包一个支持该接口的移动浏览器就能跑最适合用来验证链路和做原型。4.1 环境准备与前置条件前置条件有三条。第一设备必须是支持该接口的移动设备且浏览器版本足够新桌面浏览器基本都不支持这类能力依赖移动端的摄像头和传感器直通。第二页面必须通过 HTTPS 访问这是硬性要求本地开发时可以用自签证书起一个本地服务。第三需要用户手势触发会话启动不能在页面加载时自动调用浏览器会直接拒绝。页面骨架很简单!DOCTYPE html html head meta nameviewport contentwidthdevice-width, initial-scale1, user-scalableno style body { margin: 0; overflow: hidden; } canvas { width: 100vw; height: 100vh; display: block; } #overlay { position: fixed; left: 0; right: 0; bottom: 24px; text-align: center; color: #fff; font: 16px/1.5 sans-serif; text-shadow: 0 1px 4px rgba(0,0,0,.6); } /style /head body canvas idar-canvas/canvas div idoverlay button idstart-btn开始体验/button /div script typemodule src./main.js/script /body /html这段没什么难度但有两点值得说。一是user-scalableno必须加否则用户双指缩放会缩放整个页面而不是内容。二是遮罩层的定位要用固定定位并留出安全区域否则在带刘海的设备上按钮会被挡住。4.2 核心代码会话建立、命中测试、锚点放置主体逻辑分四步拿到 WebGL2 上下文并标记为兼容、请求会话、获取参考空间、进渲染循环。第一步初始化渲染上下文// main.js const canvas document.getElementById(ar-canvas); const gl canvas.getContext(webgl2, { xrCompatible: true, alpha: true, antialias: true }); if (!gl) { throw new Error(当前设备不支持所需的图形接口); }xrCompatible这个标记不能省省了之后会话创建会直接失败而且报错信息很不直观我第一次踩这个坑查了半小时。第二步检查支持情况并请求会话const startBtn document.getElementById(start-btn); async function startARSession() { if (!navigator.xr) { alert(当前浏览器不支持所需能力请更换设备或升级浏览器); return; } const supported await navigator.xr.isSessionSupported(immersive-ar); if (!supported) { alert(当前设备不支持沉浸式 AR 会话); return; } const session await navigator.xr.requestSession(immersive-ar, { requiredFeatures: [hit-test], optionalFeatures: [light-estimation, dom-overlay, anchors], domOverlay: { root: document.getElementById(overlay) } }); await gl.makeXRCompatible(); session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) }); return session; } startBtn.addEventListener(click, async () { const session await startARSession(); if (session) { await runLoop(session); } });这里有几个参数值得说清楚。requiredFeatures里放的是缺了就没法运行的能力hit-test是平面命中检测我们的放置逻辑全依赖它所以必须是 required。optionalFeatures里放的是有更好、没有也能降级的能力light-estimation控制光照估计没有的话就用固定的环境光dom-overlay允许把 HTML 元素叠加在 AR 画面上做 UI 面板非常方便anchors是持久锚点让物体在追踪丢失恢复后还能回到原位。第三步拿参考空间和命中检测源async function runLoop(session) { const refSpace await session.requestReferenceSpace(local); const viewerSpace await session.requestReferenceSpace(viewer); const hitTestSource await session.requestHitTestSource({ space: viewerSpace }); let anchor null; session.addEventListener(select, (event) { // 用户点击时把当前命中的位置固化成锚点 if (lastHitPose !anchor) { const frame event.frame; const hitResult lastHitPose; if (hitResult.createAnchor) { hitResult.createAnchor().then((a) { anchor a; }); } else { // 不支持持久锚点时退化为直接记录位姿 anchor { pose: hitResult.getPose(refSpace) }; } } }); let lastHitPose null; session.requestAnimationFrame(function onFrame(time, frame) { session.requestAnimationFrame(onFrame); if (hitTestSource) { const hits frame.getHitTestResults(hitTestSource); if (hits.length 0) { lastHitPose hits[0]; const pose hits[0].getPose(refSpace); // 用 pose 更新预览指示器的位置 updateReticle(pose); } else { lastHitPose null; hideReticle(); } } render(frame); }); }这段是整个 Demo 的心脏。requestReferenceSpace(local)拿到的空间原点在会话开始时的设备位置Y 轴向上这是最常用的参考空间。viewer空间是相机自身的空间用它来做命中检测意味着射线从相机发出、指向视野中心——也就是屏幕正中央的那个十字准星。如果你想让用户点哪儿放哪儿就把命中检测源换成基于屏幕坐标的输入源逻辑会复杂一些但对大屏设备更友好。select事件对应的是屏幕点击或头戴设备的确认操作。用lastHitPose而不是每次重新检测是因为点击瞬间的帧位姿和上一帧的命中结果基本一致这样写最省事。4.3 光照估计与阴影落地光照估计的关键是拿到球谐系数并喂给渲染管线async function setupLightProbe(session, refSpace) { if (!session.requestLightProbe) return null; const probe await session.requestLightProbe(); return { probe, refSpace, // 在主循环里调用取到最新的光照数据 update(frame) { if (!probe) return null; return frame.getLightEstimate(probe); } }; }取到的估计值里主要用两组数据sphericalHarmonicsCoefficients是 9 个三分量系数喂给环境光primaryLightDirection和primaryLightIntensity是主光源的方向和强度用来生成方向性阴影。这里有个实操细节球谐系数的量级和环境光强度不是一比一对应的直接拿来做乘法很容易过曝。我一般会把直流分量提取出来归一化后作为环境光颜色然后手动乘一个 0.8 到 1.2 之间的系数再按设备亮度做微调。这个系数没有标准答案靠在几种典型光照下肉眼校准——日光、室内暖光、阴天各试一次取一个折中的值。阴影的实现最省事的是用一个软边圆形贴图做投影位置取锚点在地面的投影缩放根据物体包围盒估算。这个方案在平面上的效果尚可缺点是物体倾斜或者放到非平面区域时会穿帮。稍微讲究的做法是实时阴影贴图。步骤是以主光方向建立一个正交投影相机渲染一遍场景的深度到纹理然后在正常渲染时采样这张深度纹理比较深度值。分辨率用 1024 或 2048 就够重点是阴影相机的正交范围要贴合物体包围盒——范围开太大阴影边缘全是锯齿开太小阴影会被裁掉一半。4.4 性能与体验调优Demo 跑起来之后接下来就是把它从“能跑”调到“能用”。移动端 AR 的性能瓶颈几乎总是三件事发热降频、填充率、GC 抖动。发热降频是最致命的。手机跑 AR 时摄像头、IMU、GPU 同时高负载十分钟后机身温度上来系统就会降频帧率从 60 掉到 30追踪也跟着抖。缓解手段有把渲染分辨率降下来用XRWebGLLayer.framebufferScaleFactor设成 0.8 或 0.7视觉上几乎无感但省一大截填充率限制阴影贴图更新频率不用每帧更新隔帧更新肉眼看不出把不动的静态物体合并成一个批次减少绘制调用。GC 抖动是个隐形杀手。三维渲染的循环里如果每帧都new一个矩阵或向量一分钟就产生上千个临时对象触发垃圾回收时会出现明显卡顿。解决办法是把常用的向量、矩阵对象在循环外创建好循环里只改值不新建。这个优化做不做帧时间的方差能差出一倍。还有纹理压缩。一张 2048×2048 的 RGBA 未压缩纹理占 16MB 显存压缩成 ASTC 之后可能只有 2MB 到 4MB。这不是优化这是必需品尤其在同时加载多个模型的场景里不压缩直接就是内存不够。5. 典型场景的落地做法技术讲透了最后看几个真实场景里怎么把这套东西用起来。每个场景的解法重点完全不同照抄别人的方案基本都会翻车。5.1 工业巡检与远程协作这个场景的核心诉求是“让不在现场的人看到现场并且能指着某处说话”。典型做法是现场人员戴头戴设备把第一视角画面实时传回远端专家在画面上画圈标注标注以空间锚点的形式固定在被指的设备部件上现场人员转头再转回来标注还在原处。这里的技术重点是空间锚点的持久化和多人共享。标注必须固定在世界坐标系里而不是钉在屏幕上否则现场人员一动标注就跟着跑完全没法用。实现上需要一套空间地图设备扫描环境生成特征地图把锚点存在地图坐标系里远端设备加载同一份地图后锚点就能对齐到同一个物理位置。实操中的坑集中在初始对齐。两台设备要进同一个坐标系通常需要一个共享的初始化过程比如双方同时看同一个标记或者扫同一个区域。这一步如果做得不顺畅用户会直接放弃。我的经验是把初始化做成一次性的、有明确进度提示的流程而且失败时给出具体的失败原因光线不足、移动太快、纹理太少而不是一句“初始化失败请重试”。5.2 零售与展示这个场景追求的是“转化”所以重点全在真实感和流畅度上技术难点反而不多。核心是三件事模型要像、加载要快、摆放要顺手。模型要像靠的是 PBR 材质和光照匹配。金属、皮革、玻璃这三种材质最容易露馅。金属需要环境反射得配一张环境贴图皮革需要法线和粗糙度贴图玻璃需要透射和折射移动端基本只能靠贴图近似。我的建议是在美术阶段就用目标设备预览不要在设计软件里看着好就直接导出。加载要快靠的是分档加载。首屏只加载低模和一张缩略贴图用户摆好位置之后再异步换成高模。这个“先粗后精”的策略能把首屏等待从五六秒压到一秒以内体验差别巨大。摆放要顺手靠的是把交互限制在平面内。允许用户旋转和平面移动但不允许自由上下调整高度——因为在平面上摆放的东西本来就应该贴着平面自由抬高只会让用户把东西摆到半空中然后觉得“这玩意儿怎么飘着”。5.3 教育培训教育场景的诉求是“把看不见的东西看见”。机械拆装、人体结构、分子模型、电磁场分布这些抽象内容做成 AR 之后理解成本会大幅下降。这个场景最需要注意的是节奏控制。教学内容的 AR 化不是把模型丢出来就完事而是要有明确的引导序列先看整体再拆解再逐个讲解最后做检验。每一段都要有“用户要做什么”的明确指令否则用户拿到模型之后只会转两圈就失去兴趣。另一个细节是多人共用一台设备。教室场景里常见的情况是一个班几十人轮流看设备的初始化和复位必须快。我一般会把内容做成“固定锚点 快速复位”的形式老师事先在讲台上贴一个标记所有内容都锚定在这个标记上学生拿起来就能看放下就复位不需要每次重新扫描环境。6. 常见问题与排查实录这一节是我这些年踩过的坑的汇总按症状分类方便你对照排查。6.1 追踪漂移和抖动症状是模型放在桌上手机缓慢平移一圈回来模型偏移了几厘米。这是 VIO 累积误差的正常表现不可能完全消除只能控制。排查顺序先看是不是光线太暗。视觉追踪在照度低于 50 lux 的环境里基本失效这不是算法问题是物理限制。再看是不是纹理太少。纯白墙面、纯色桌面、玻璃桌面这些表面提不出足够特征点追踪会飘。解决办法是在场景里放一些有纹理的参照物或者引导用户多角度看。如果环境没问题还是飘就要查 IMU 标定了。前面说过零偏没标定会让追踪在启动阶段就带着系统性偏差。另外要检查时间同步——图像和 IMU 数据的时间戳如果没对齐融合出来的结果一定是错的而且这种错误在慢速运动时看不出来一旦快速转动就暴露。6.2 物体漂浮和穿模模型看着“浮在空中”最常见的原因不是追踪问题而是锚点高度和视觉地面对不上。平面检测给出的平面是一个数学拟合结果和肉眼看到的地面可能有几毫米到一两厘米的偏差物体越大这个偏差越明显。处理办法有两条一是把物体稍微下沉几毫米用视觉上的“压地感”掩盖偏差二是在物体底部加一个接触阴影人眼对阴影的缺失极其敏感加了阴影之后即使有几毫米的偏差也不容易察觉。穿模问题通常出在遮挡处理缺失。如果场景里有真实物体挡在虚拟物体前面而系统没有做深度遮挡虚拟物体会直接“画”在真实物体上面穿帮感极强。移动端可行的做法是用深度传感器数据做粗略遮挡或者干脆在设计上避开遮挡关系——把虚拟物体放在前景空旷的位置从内容设计层面规避技术短板这是很实用的思路。6.3 发热掉帧的连锁反应这个问题的麻烦之处在于它是连锁的发热导致降频降频导致帧率下降帧率下降导致追踪更新变慢追踪变慢导致物体滞后于相机运动用户就觉得“模型跟不上手”。排查的时候先确认是不是热导致的连续运行十五分钟记录帧率和设备温度如果帧率随时间明显下滑那就是热。对策前面说过降分辨率、减批次、降低阴影更新频率。还有一个容易被忽略的是摄像头采集分辨率很多框架默认开高分辨率采集其实追踪用 640×480 就足够降下来能省不少功耗。6.4 常见问题速查表症状最可能原因快速验证方法处理方向模型整体偏移坐标系约定不一致放一个已知朝向的测试立方体检查 Y/Z 轴与手性的转换模型缩放异常建模单位与运行时单位不匹配打印模型包围盒尺寸统一按米为单位导出追踪几秒后丢失环境纹理不足或光照过暗换到有纹理的明亮环境重试增加参照物引导用户移动物体边缘发虚渲染分辨率不足临时把缩放系数调到 1.0提高渲染分辨率或接受折中阴影方向不对主光方向未做坐标系转换打印光方向向量转换到世界系后再使用长时间使用变卡发热降频或内存增长记录帧时间曲线与内存占用降分辨率、复用对象、压缩纹理点击选不中物体射线拾取用了屏幕坐标未归一化打印射线起点方向归一化到 [-1,1] 再构造射线加载白屏数秒首屏资源过大看网络面板的传输体积分档加载先低模后高模6.5 几条不值钱但很省时间的经验第一条永远先做端到端的最小闭环。哪怕模型只是个灰色方块也要先把“检测平面 → 放置 → 追踪 → 渲染”这条链路跑通再去接美术资源。我见过太多团队反过来做美术资源做了两个月接进来发现追踪方案选错了全部重来。第二条在现场测不在工位测。工位的灯光、桌面材质、空间大小都和真实场景不一样。AR 是对环境极度敏感的技术实验室里跑得再好到了实际场地照样可能翻车。我现在的习惯是方案定下来第一周就去现场跑一遍早发现问题早改。第三条给追踪失败设计好兜底。追踪一定会失败问题是怎么失败得好看。比较好的做法是检测到追踪质量下降时把虚拟物体淡出并提示用户“请缓慢移动设备重新扫描”而不是让模型卡在原地抽搐。这个体验细节用户未必会夸但做不好一定会骂。第四条性能预算要在项目第一天就定死。帧率目标、单模型面数上限、纹理总预算、同时加载的模型数量这几个数一旦定下来就不要轻易破例。AR 项目的性能崩溃从来不是某一个大问题导致的而是每个环节都多花了一点点最后加起来超了。最后分享一个我做内容评审时的小方法把设备交给一个完全没接触过这个项目的人什么都不说看他三十秒内能不能自己搞清楚要干什么。如果他要问“这个怎么用”那说明引导设计有问题跟技术实现没关系。这个方法比任何体验指标都直观也最能暴露真实问题。
阅读完成 · 觉得有帮助?