想象一下,你拖着一个行李箱去环游世界:美国的插座是扁脚,欧洲是圆脚,日本插座形状也不一样。如果每到一个国家就重新买一套充电器,行李箱再大也装不下。聪明的做法是带一个万能转换插头——不管哪个国家的插座,插进去都能供电。 AR 开发正面临同样的烦恼:Android 阵营用的是 Google 的 ARCore,iOS 阵营用的是 Apple 的 ARKit,两套 API、两套实现。如果你要同时上架两个平台,就得写两套代码、请两拨人、修两倍的 bug。而 Unity 官方给出的答案,就是 AR Foundation——那个替你省下整个行李箱空间的万能插头。它把 ARCore 和 ARKit 的差异全部封装起来,让你用同一份 C# 代码,一键打包 Android 和 iOS,实现真正的“一套代码,双端运行”。 这套“万能插头”是怎么工作的?我们要装什么样的转换模块?它如何找到现实世界的平面、如何把虚拟物体稳稳地“插牢”在真实场景里,又是如何感知光线的变化?这篇文章就带你一步步拆解。
对 AR 从业者来说,AR Foundation 是打通 Android 与 iOS 的桥梁,掌握它能让你用一半人力交付双端 AR 产品,把时间从“适配平台”挪到“打磨体验”上。
目录
可视化演示
展示AR Foundation的平面检测与锚点定位、ARCore/ARKit统一架构、环境光照估计与手势交互三大核心能力。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+_captions.plane+'
跨平台架构:一个插头,两个国家的插座
你拖着行李箱走进美国酒店,掏出万能插头插进扁脚插座,手机开始充电;第二天飞到欧洲,换一个方向,圆脚插座同样能充电。你不需要带两套充电器,因为转换插头把“墙后面的电”统一成了同一个输出。AR 开发也如此:Android 的墙后面是 ARCore,iOS 的墙后面是 ARKit,两者追踪算法不同、接口不同,但你的产品希望跑到两个国家。
ARCore 是 Google 的移动 AR 平台,基于特征点与惯性测量实现位姿追踪;ARKit 是 Apple 的 AR 平台,强调视觉惯性里程计与场景理解。如果没有 AR Foundation,你要分别写两套识别、两套渲染调度。AR Foundation 是 Unity 官方提供的抽象层:它把 ARCore 和 ARKit 的能力收敛成统一的 C# 接口,ARPlaneManager、ARAnchorManager、ARLightEstimate 等组件在不同平台上自动路由到对应的原生实现。
1. 代码输入:Code_C# 代表你写的全部 C# 脚本和场景文件,它们只与 AR Foundation 的组件打交道。
2. 封装函数:ARF() 是 AR Foundation 这层“转换器”,负责把每一条组件调用翻译给底层 SDK。
3. 双端产物:等式右侧是输出集合,APK 跑在 Android 上并调用 ARCore,IPA 跑在 iOS 上并调用 ARKit,中间不需要再改业务代码。
用上 AR Foundation 以后,你的团队就像只带一个万能插头的旅行者:到了 Android 国家插 ARCore 口,到了 iOS 国家插 ARKit 口,电器(应用)照常工作。这就是“跨平台架构”的核心价值:用一次开发,覆盖两个生态。
ARF( Code_C# ) = { APK_ARCore, IPA_ARKit }环境搭建:给插头配上转换模块
万能插头买回来不能直接用,得先确认它包含哪些国家插脚的模块,也得看一下目标国家的电压范围。AR Foundation 同样如此:它不是装上就万事大吉,而是需要你完成“选模块”和“确认电压”两步,AR 会话才能真正跑起来。
在 Unity 里,你要在 Package Manager 中安装三个东西:AR Foundation 本体,以及 ARCore XR Plugin(去 Android 的转换模块)和 ARKit XR Plugin(去 iOS 的转换模块)。随后在 Build Settings 里把目标平台切到 Android 或 iOS,设置好最低 API Level(Android 建议 24 以上)和相机权限描述。接下来在场景中创建 AR Session 和 AR Session Origin 两个核心对象:前者负责会话的生命周期,后者负责把虚拟内容对齐到真实空间原点。
把环境搭建比作“拆开插头包装、装入各国模块”,那么 AR Session 状态机就是插头上的指示灯:红灯代表正在找信号,绿灯代表已经锁定,灰灯代表临时停电。搭建对了,后面所有功能才有稳定的地基。
- 会话变量
S_session对应场景里挂载着 AR Session 组件的对象,它保存着当前 AR 运行状态。 - 属于关系
∈表示会话状态必须从右侧集合中选取,不能同时出现在两个状态。 - 状态枚举
None表示尚未启动;Init表示正在加载原生 SDK;Tracking表示相机正在稳定追踪环境;Paused表示 App 切到后台暂时冻结。
平面检测:让插头“摸”到插座的位置
万能插头的金属弹片需要探到插座孔眼,才能咬合导电;AR 应用的虚拟物体也需要找到现实中可以“插下去”的表面,这个表面在 AR 里叫平面。平面检测做的就是:让手机摄像头在画面里找出桌面、地板、墙面这些平坦区域。
AR Foundation 的平面检测由 AR Plane Manager 驱动。原生 SDK 会持续提取画面的特征点,把这些点拟合成平面,并返回平面的中心、法线方向、边界点和尺寸。Unity 中会收到三种事件:平面新增、平面更新、平面移除。开发者通常会把检测到的平面渲染成一个半透明网格,类似可视化演示里的地面网格。
平面检测像是插头的试探动作:先伸出去碰一碰,碰到硬表面,再根据回馈确认“这里可以固定”。给你的用户一个简单的引导——“请缓慢移动手机扫描桌面”,往往比任何算法调优都更有效。
- 法向量
n是平面的朝向。桌面朝上,n 就大致指向天空;墙面朝前,n 就指向房间内侧。 - 位置偏移
x - c表示平面上任意一点到中心c的偏移向量。 - 点积为零
n · (x - c) = 0的含义是偏移向量与法向量垂直,这正是“平面上所有点”的数学表达。 - 置信度
s_i在 0 到 1 之间,越接近 1 代表平面越可靠,通常高于 0.5 才值得在其上放东西。
锚点系统:插入后锁紧卡扣
插头插进去之后,如果随时可能被碰掉,充电就会中断。所以好的转换插头都设计了卡扣或夹紧结构,插进去就牢牢咬住。AR 世界的“卡扣”叫锚点:它把虚拟物体钉在真实世界的一个固定位置上,即使手机移开再回来,物体也应该回到原来的地方。
AR Anchor Manager 负责锚点的创建与维护。你可以把任意 GameObject 挂上 AR Anchor 组件,AR Foundation 会在它所在的位置生成锚点;更常见的做法是先用射线检测平面,拿到命中点,再在那里放置带锚点的预制体,虚拟物体就“长”在了真实桌面上。锚点会实时接收底层跟踪数据,当相机重新定位时,锚点也会自动纠偏。
锚点是虚拟世界的地钉:卡扣没扣紧,物体就会漂移;扣紧了,杯子和桌面之间就像真的放了点胶。在实现 AR 试穿或室内导航时,先找到可信锚点,再谈后续体验。
- 世界到相机
^W T_C是相机在世界坐标里的位姿,也就是“手机现在在哪、在看哪”。 - 相机到锚点
^C T_A是锚点相对相机的偏移,由射线命中结果计算得到。 - 矩阵相乘两个变换矩阵相乘,把相对位姿叠加到相机位姿上,算出的
^W T_A就是锚点的世界坐标。
光照估计:跟着当地亮度自动调节
你带着一个可调亮度的台灯环游世界,到了美国电压低,灯变暗;到了欧洲电压高,灯变亮。如果台灯不懂“自适应”,它要么暗得看不见,要么亮得刺眼。AR 里的虚拟物体也一样:在一个明亮的客厅里放置的 3D 台灯,如果材质亮度是固定值,放到昏暗的走廊里就会显得非常“假”。
ARCore 和 ARKit 都提供光照估计:从相机画面中解析出当前环境光的平均亮度和色温(冷光偏蓝,暖光偏黄)。Unity 的 AR Light 组件会自动把这两项数据同步到场景主光,材质再配合反射探针(Environment Probe),让虚拟物体表面出现与真实环境一致的明暗过渡和反光。
这就像台灯内置了一个光传感器:房间亮它就亮,房间暖它就暖。做 AR 试穿时,衣服色调必须随环境变化,光照估计是“虚实难分”的最后一块拼图。
- 基础颜色
Color_base是这个物体原本的材质颜色,比如一件红色 T 恤的 RGB 值。 - 亮度系数
L_real / L_max把真实光的强度压到 0 到 1 之间,光越强,虚拟物体越亮。 - 色温映射
CT(T_real)根据环境色温调整冷暖,让白色物体在暖光下微微泛黄、在冷光下泛蓝。
手势交互:用手指给虚拟物体下达指令
万能插头上有按钮、有指示灯,你通过按键让插头完成切换;AR 手机没有物理按钮,触屏就成了天然的控制面板。用户单击、拖动、双指捏合,本质上都是在向虚拟世界下达指令。
AR Foundation 的手势交互通常分成三步:第一步通过 Unity 的 Input 系统拿到触摸点;第二步从相机发射一条射线,求出射线与检测平面的交点;第三步把这一世界坐标映射成一个操作,比如放置物体、移动锚点。捏合手势则用双指间距的变化控制缩放,旋转手势用双指连线角度的变化控制转动。为了体验好,还要加上高亮、震动、音效这类即时反馈。
手势交互就是那排按键:点一下是确定,拖一下是移动,捏一下是放大。把交互反馈做足,虚拟物体就像握在用户手里一样真实。
- 屏幕坐标
u, v是触摸点的像素坐标,原点在画面左上角。 - 内参归一
(u - c_x) / f_x和(v - c_y) / f_y把像素位置换算成相机坐标系下的方向余弦。 - 相机逆变换
T_cam^{-1}把方向从相机坐标系变换到世界坐标系,等于把“从镜头看出去”转译成“从世界上看”。 - 深度系数
λ表示沿射线走的距离;配合之前检测到的平面,就能算出射线与平面的实际交点。
AI 应用场景
虚拟试衣间
阶段:内容放置与场景构建
解决的问题:传统电商试穿只能看静态图片,用户不知道衣服上身效果;如果分别开发 Android 和 iOS 两套 AR 试穿,成本直接翻倍。用 AR Foundation 一套代码可以覆盖双端,结合平面检测把 3D 服装摆在用户前方,再用手势拖拽旋转查看。
具体案例:某服装品牌在 App 内上线虚拟试衣间:用户打开相机,系统自动检测面前的平面,出现一个半透明的可点击衣架;点击后衣服模型立即“穿”到用户身上,并且利用光照估计让衣服颜色随室内灯光自动调节。
商场室内导航
阶段:定位与导航
解决的问题:商场里 GPS 漂移严重,传统导航无法定位到楼层内。AR 导航利用平面检测判断地面,再用锚点把方向箭头钉在真实过道上,用户只要举起手机就能看到路标箭头,而不是盯着小地图反复辨认。
具体案例:某大型购物中心部署 AR 导航:系统识别地面平面后,沿线放置一串带锚点的箭头模型;用户走到哪,箭头就在当前锚点上闪烁,同时根据环境光照自动调整亮度和对比度,保证在玻璃穹顶下依然清晰可见。
AR 教育科普
阶段:视觉交互与反馈
解决的问题:书本上的行星、人体器官是二维平面图,学生很难建立空间想象。AR 教育应用把抽象知识变成可触摸的立体模型,帮助理解。
具体案例:某科普读物配套 App 采用 AR Foundation:扫描书页后,在识别出的桌面上浮现太阳系模型,学生用手指拖动行星绕太阳旋转、双指捏合放大观察土星环,模型还会根据书桌附近的光线调整亮度,仿若真实物体摆在桌面上。
Python 演示
import mathfrom dataclasses import dataclassfrom typing import List, Optional @dataclassclass Plane: center: tuple normal: tuple area: float confidence: float @dataclassclass Light: intensity: float color_temp: float @dataclassclass Anchor: pose: tuple virtual_object: str = "cube" class PlatformAdapter: """平台适配接口, 类比 AR Foundation 的抽象层""" def detect_planes(self, frame): raise NotImplementedError def estimate_light(self, frame): raise NotImplementedError def create_anchor(self, pose): raise NotImplementedError class AndroidAdapter(PlatformAdapter): def detect_planes(self, frame): return frame["android_planes"] def estimate_light(self, frame): return frame["android_light"] def create_anchor(self, pose): return Anchor(pose, "android_cube") class iOSAdapter(PlatformAdapter): def detect_planes(self, frame): return frame["ios_planes"] def estimate_light(self, frame): return frame["ios_light"] def create_anchor(self, pose): return Anchor(pose, "ios_cube") class ARFoundationSession: def __init__(self, adapter): self.adapter = adapter self.anchors = [] def process_frame(self, frame): planes = self.adapter.detect_planes(frame) light = self.adapter.estimate_light(frame) best = None for p in planes: if p.confidence > 0.5 and (best is None or p.area > best.area): best = p if best is not None and light.intensity > 1.0: anchor = self.adapter.create_anchor(best.center) anchor.virtual_object = "cube" self.anchors.append(anchor) return anchor return None def demo(): android_frame = { "android_planes": [Plane((0, 0, 0), (0, 1, 0), 1.2, 0.9)], "android_light": Light(320, 5200), } ios_frame = { "ios_planes": [Plane((0, 0, 0), (0, 1, 0), 1.5, 0.95)], "ios_light": Light(310, 5100), } arf_a = ARFoundationSession(AndroidAdapter()) arf_i = ARFoundationSession(iOSAdapter()) result_a = arf_a.process_frame(android_frame) result_i = arf_i.process_frame(ios_frame) print("Android 锚点:", result_a.virtual_object if result_a else None) print("iOS 锚点:", result_i.virtual_object if result_i else None) if __name__ == "__main__": demo()
class PlatformAdapter:定义平台适配接口,对应 AR Foundation 的抽象接口层,业务代码只面对它。class AndroidAdapter(PlatformAdapter):Android 端从 ARCore 帧数据中提取平面与光照,对应 ARCore XR Plugin。class iOSAdapter(PlatformAdapter):iOS 端从 ARKit 帧数据中提取相同字段,对应 ARKit XR Plugin。class ARFoundationSession:统一会话类,业务开发者只和它打交道。best = None先假定没有可用平面,再遍历所有候选平面挑面积最大且置信度 > 0.5 的那个。anchor = self.adapter.create_anchor(best.center)在最优平面中心创建锚点,对应 Unity 中点击桌面后生成 AR Anchor。result_a = arf_a.process_frame(android_frame)同一个接口分别处理两个平台的数据,获得一致的锚点结果。
常见误区
误区我以为 AR Foundation 就像 ARCore 或 ARKit 那样的原生 SDK,装上它之后,就不需要再安装 ARCore 和 ARKit 了,甚至可以在不支持的手机上跑 AR。
正解AR Foundation 本身不实现跟踪算法,它只是 Unity 的抽象层,真正的追踪、平面识别、光照估计都由底层 ARCore / ARKit 完成。就像万能插头自己不会发电,墙后面必须有电才行。Android 必须支持 ARCore(或安装 ARCore 服务),iOS 必须支持 ARKit,否则会话会直接不可用。
为何常见:因为通过 Package Manager 安装 AR Foundation 后一切看起来都“自动工作”,容易让人误以为它是一个自包含的 SDK。
误区我以为多包一层 AR Foundation 一定会让应用变卡,帧率下降明显,不如直接用原生 SDK 写两套。
正解AR Foundation 的抽象层开销极小,主要成本在原生会话和渲染上。实际项目中更容易遇到的是过度使用射线检测、每帧创建大量 GameObject、材质球复制过多导致的性能问题。
为何常见:很多人把性能问题归咎于框架,其实是自己的渲染逻辑没优化。
误区我以为把 AR Session 组件放进场景,应用启动后它就会一直稳定工作,不需要考虑后台切换。
正解App 切到后台时,相机连接会被系统暂停,AR Session 也会自动进入 Paused。回到前台时,会话需要恢复 Tracking,必要时调用 ARSession.Reset() 重新初始化定位。忽略这些回调,用户经常会看到“画面卡住”或“虚拟物体乱飘”。
为何常见:在编辑器的 Play Mode 里不会触发手机后台生命周期,所以很多问题直到真机上才暴露。
误区我以为锚点就像现实里钉了钉子一样,放上去就不会动,可以一直信赖。
正解锚点依赖 SLAM 对特征点的持续观测。当光线突然变化、镜头剧烈运动、场景里出现大量人群遮挡时,锚点可能会漂移甚至丢失。开发时要监听锚点的跟踪状态变化,及时提示用户重新扫描。
为何常见:官方 Demo 大多在纹理丰富、光线稳定的演示环境里运行,看得多了就以为这是常态。
误区我以为只要现实中有桌面、地板、白墙,AR 就能立刻检测出一个平面来放东西。
正解平面检测靠特征点做拟合,所以低纹理的纯白墙面、玻璃幕墙、反光大理石都很难命中。真实项目中往往要引导用户对准有贴纸、瓷砖缝隙、地毯纹理的区域扫一圈,等平面回调出现后再放置物体。
为何常见:用户默认手机摄像头和人眼一样智能,但特征点算法恰恰怕“太干净”的表面。
面试题
Q1. AR Foundation 在 Unity 中的核心定位是什么?
- A.独立的 3D 渲染引擎
- B.统一封装的抽象接口层← 正确答案
- C.替代原生 SDK 的插件
- D.只能做手势识别的模块
答案:B。AR Foundation 是 Unity 官方提供的抽象接口层,它把 ARCore 和 ARKit 的差异封装成统一 Unity 组件,类似万能插头统一各国插座规格。A 说它是 3D 引擎不对;C 错在它并不能替代原生 SDK,底层仍然要调用 ARCore/ARKit;D 完全低估了它的职责。
Q2. AR Foundation 底层真正对接的移动端平台有哪两个?
- A.同时支持 ARCore 和 ARKit← 正确答案
- B.只支持 Google ARCore
- C.只支持 Apple ARKit
- D.支持 OpenXR 和 SteamVR
答案:A。AR Foundation 在 Android 端对接 ARCore,在 iOS 端对接 ARKit,所以正确答案是 A。B、C 都只覆盖单平台,D 中的 OpenXR/SteamVR 面向 VR/PC 场景,不是移动 AR 的主阵地。
Q3. 下面哪一项是 AR Session 的合法状态?
- A.None、Init、Tracking、Paused← 正确答案
- B.Start、Run、Stop、Reset
- C.On、Off、Suspend、Exit
- D.Load、Create、Update、Delete
答案:A。AR Session 是状态机,状态依次为 None(未启动)、Init(初始化)、Tracking(跟踪中)、Paused(已暂停),A 正确。B/C/D 都是通用编程动词,不属于 AR Session 状态枚举。
Q4. 平面检测更依赖以下哪些数据?
- A.特征点分布← 正确答案
- B.相机实时位姿← 正确答案
- C.环境光照强度
- D.设备剩余电量
答案:AB。平面检测依托视觉-惯性 SLAM:特征点分布用于拟合几何平面,相机实时位姿用于保证连续帧的数据一致性,因此选 A、B。环境光照强度属于光照估计模块,剩余电量与平面检测无关。
Q5. 将虚拟物体稳定放到检测平面上的标准做法是?
- A.把物体放在原点附近
- B.射线命中平面再创锚点← 正确答案
- C.随机撒在世界场景中
- D.固定到屏幕中心位置
答案:B。标准流程是从相机发射射线与平面求交,在命中点生成锚点,然后把虚拟物体作为锚点的子物体,这样物体才会稳定地钉在真实表面上。A/C 无法贴合现实表面,D 会让物体跟着相机视角移动,都不能保持稳定。
Q6. AR 光照估计通常可以提供哪些信息?
- A.环境光亮度← 正确答案
- B.色温冷暖← 正确答案
- C.反射探针参数← 正确答案
- D.材质球强度
答案:ABC。ARCore/ARKit 的光照估计能给出环境光亮度(lux)与色温(Kelvin),ARKit 还可提供环境反射探针,让金属材质反射出真实场景,所以选 A/B/C。材质球强度是客户端自己定义的,不是 SDK 输出的数据。
Q7. 屏幕触摸点转成 3D 世界坐标,最关键的操作是?
- A.触点屏幕坐标加偏移
- B.射线与平面求交运算← 正确答案
- C.读取罗盘方向数据
- D.使用线性回归拟合
答案:B。正确做法是构造一条从相机穿过触摸点的射线,与已检测平面求交,得到世界坐标。A 不能产生深度信息,C 属于方向传感器,D 是统计手段,都不适用于坐标投影。
Q8. 下列哪种环境最可能导致 AR 锚点漂移?
- A.低纹理且光照突变场景← 正确答案
- B.高纹理且光照稳定场景
- C.大而平坦的白墙墙面
- D.有丰富特征点的桌面
答案:A。锚点稳定依赖特征点匹配,低纹理让特征缺失,光照突变又让曝光剧烈变化,两者叠加会导致跟踪瞬间丢失,最危险。C 虽然纹理少但光照稳定时还能维持;B、D 都是理想场景。
Q9. Unity 场景中要让 AR 真正运行,至少需要哪个对象?
- A.主相机和灯光组件
- B.AR Session 组件← 正确答案
- C.Canvas 和 EventSystem
- D.网络摄像头模块
答案:B。AR Session 是连接原生 ARCore/ARKit 的总开关,没有它底层会话不会启动。主相机、灯光、Canvas 都是常规 Unity 组件,不是 AR 运行的必要条件;D 也不是 AR 的启动入口。
Q10. 下面哪些手势交互常见于 AR Foundation 应用?
- A.单击放置虚拟内容← 正确答案
- B.双指捏合缩放物体← 正确答案
- C.单指拖拽实现平移← 正确答案
- D.长按调整相机焦距
答案:ABC。单击放置、双指缩放、单指拖拽是 AR 应用最常用的一套手势,配合射线检测即可完成虚拟物体的增删改。D 的长按调焦距在手机 AR 里不成立,相机焦距由硬件决定,日常交互不会通过长按来改变。