Streetscape Geometry 获取建筑地形几何数据
Streetscape Geometry 是什么
Streetscape Geometry(街景几何)是 Geospatial API 的附加能力——它让应用不仅知道"手机在哪里",还知道周围有什么东西:建筑物的 3D 形状、地形的起伏、路面的高度变化等。这些数据来自 Google 街景采集时同步获取的 LiDAR 扫描和立体视觉点云。
获取到的几何数据以多边形网格(Mesh)的形式返回,可以直接用于:
- 遮挡渲染:虚拟物体被真实建筑物挡住
- 碰撞检测:让虚拟球在真实建筑物前反弹
- 路径规划:在 AR 导航中让箭头绕过建筑物
- 可视化:渲染一个半透明的城市 3D 模型作为底图
启用 Streetscape Geometry
Streetscape Geometry 需要 Geospatial API 先启用,再单独开启:
session.configure(
Session.Config(context).apply {
setGeospatialMode(Session.Config.GeospatialMode.ENABLED)
// 启用 Streetscape Geometry
setStreetscapeGeometryMode(
Session.Config.StreetscapeGeometryMode.ENABLED
)
}
)
// 检查是否支持
val supported = session.isStreetscapeGeometryModeSupported(
Session.Config.StreetscapeGeometryMode.ENABLED
)
Streetscape Geometry 对设备和网络的要求比单纯 Geospatial API 更高——它需要下载并处理更大的几何数据集。
获取 Streetscape 数据
Streetscape Geometry 作为一种特殊的 Trackable 存在——StreetscapeGeometry:
session.update(frame)
// 获取当前所有可跟踪的 Streetscape 几何
val geometries = session.getAllTrackables<StreetscapeGeometry>()
geometries.forEach { geometry ->
// geometry 有两种类型
when (geometry.type) {
StreetscapeGeometry.Type.BUILDING -> {
// 建筑物几何(通常是规则的 Box 或 Mesh)
val mesh = geometry.mesh
}
StreetscapeGeometry.Type.TERRAIN -> {
// 地形几何(起伏的 Mesh)
val mesh = geometry.mesh
}
}
// mesh.vertices: FloatArray,每个顶点 3 个浮点 (x, y, z) 世界坐标
// mesh.triangles: IntArray,三角形索引,每 3 个索引定义一个三角形面
// mesh.vertexNormals: FloatArray,每个顶点的法线方向
}
返回的 Mesh 数据是三角形网格——和 OpenGL/Unity/Mesh 引擎的 mesh 格式兼容,可以直接用来创建 Collider 或 MeshFilter。
在 Unity 中使用 Streetscape
Unity AR Foundation 需要 Geospatial API 的扩展包(Unity.XR.ARFoundation + Unity.XR.ARCoreExtensions):
// 订阅 Streetscape 更新
ARStreetscapeGeometryManager.Subsystem.frameReceived += OnFrameReceived;
void OnFrameReceived(ARStreetscapeGeoFrameEventArgs args)
{
foreach (var geo in args.geometries)
{
// geo.type = Building 或 Terrain
// geo.mesh 的顶点、三角形数组
CreateMeshCollider(geo);
}
}
创建 Collider 做碰撞检测
void CreateMeshCollider(ARStreetscapeGeometry geo)
{
var go = new GameObject($"Streetscape_{geo.id}");
var meshFilter = go.AddComponent<MeshFilter>();
var meshCollider = go.AddComponent<MeshCollider>();
var mesh = new Mesh();
mesh.vertices = geo.mesh.vertices;
mesh.triangles = geo.mesh.triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
这样你的虚拟物体(比如虚拟球)就能和真实建筑物做物理碰撞了。
遮挡渲染集成
Streetscape Geometry 配合 Depth API 可以实现室外遮挡——普通 Depth API 在远距离的遮挡精度有限,Streetscape Geometry 的建筑 Mesh 提供了额外的深度参考:
渲染流程:
1. 把 Streetscape Geometry 的所有 Mesh 渲染到深度缓冲(不写颜色)
2. 叠加 Depth API 的深度数据(作为补充)
3. 渲染虚拟物体时用这个合并的深度缓冲做遮挡测试
Unity 中可以用 AROcclusionManager + 自定义 RenderFeature 实现。
数据质量与限制
| 因素 | 说明 |
|---|---|
| 覆盖范围 | 有 Street View + 建筑数据的区域才返回 BUILDING 类型;几乎所有区域都有 TERRAIN 类型 |
| 几何精度 | 建筑物几何精度通常在 1~3 米(来自街景 LiDAR) |
| 更新频率 | Google 街景更新时同步更新几何;新建筑可能要等下次街景拍摄 |
| 资源消耗 | Mesh 数据量大——一个街区的建筑 Mesh 可能有数十万顶点 |
| 内存管理 | 注意只处理 VPS 定位附近的几何,远离后丢弃 |
Geospatial Depth:更进一步
Geospatial Depth 是 Streetscape Geometry + 本地 Depth API 的融合:
Geospatial Depth = Streetscape 几何 + 设备本地深度传感器
对于设备支持的区域,Geospatial Depth 能输出比单纯 Depth API 更精确的室外深度数据——因为 Streetscape Geometry 弥补了被动深度在远距离的精度下降。
session.configure(
Session.Config(context).apply {
setDepthMode(Session.Config.DepthMode.AUTOMATIC)
setGeospatialDepthMode(
Session.Config.GeospatialDepthMode.DISABLED
// 某些版本还在实验中
)
}
)
实际案例:Balloon Pop
Google 自家的 Balloon Pop(开源 AR 游戏)用 Geospatial API + Streetscape Geometry 实现了:
- 玩家在真实世界放虚拟气球靶
- 用物理引擎让虚拟球在真实地形上弹跳
- 球被真实墙壁和建筑物挡住(碰撞检测用 Streetscape Building Mesh)
这个项目是理解 Streetscape Geometry 用法的最佳参考。