[visionOS] ARKitで検出した平面を可視化する
公式チュートリアルのコードは何が足りないか
visionOSにおけるARKitの平面検出については公式のチュートリアルがある。
こんな感じで、PlaneAnchor
の classification
を「テキストで」可視化するコードが載っている:
func updatePlane(_ anchor: PlaneAnchor) {
if entityMap[anchor.id] == nil {
// Add a new entity to represent this plane.
let entity = ModelEntity(
mesh: .generateText(anchor.classification.description)
)
entityMap[anchor.id] = entity
rootEntity.addChild(entity)
}
entityMap[anchor.id]?.transform = Transform(matrix: anchor.originFromAnchorTransform)
}
なので、同ページに載ってる動画のような、平面を色付けして可視化するようなコードにはなっていない。
Apple Developer Documentationの "Placing content on detected planes" より(visionOSのスクリーンキャプチャーではありません)
単に generatePlane すれば良いのでは?
検出した平面を可視化するには、シンプルに、ModelEntity
のイニシャライザに渡すメッシュ(MeshResource
型)として、generatePlane
で生成した平面メッシュを渡せばよい。
let entity = ModelEntity(mesh: .generatePlane(width: anchor.geometry.extent.width, height: anchor.geometry.extent.height), materials: [material])
・・・のだが、実はこれだと実際の平面とオーバーレイした平面エンティティがずれる。
Debug Visualizationメニューの "Surfaces" を有効化するとわかるのだが、
- Debug Visualizationで可視化されているSurfaces(=検出しているはずの平面)
-
ModelEntity
で表示している平面メッシュ
これらがずれてしまう。(ARKitは実機でしか動作しないので現時点ではスクショを載せられないが、試してみるとわかる)
PlaneAnchor
を可視化するには
正しく なぜ平面がずれるかというと、問題は PlaneAnchor.Geometry.Extent
の anchorFromExtentTransform
を適用していないことにある。
/// Get the transform from the plane extent to the plane anchor’s coordinate system.
public var anchorFromExtentTransform: simd_float4x4 { get }
テキストだけで可視化している場合は平面としての形状は使用していないので、エンティティのtransformに PlaneAnchor の originFromAnchorTransform を適用するだけでOKだった。
entityMap[anchor.id]?.transform = Transform(matrix: anchor.originFromAnchorTransform)
しかし、平面の形状としてPlaneAnchor.Geometry.Extent
を使用するのであれば、Extentからアンカーへの変換行列である anchorFromExtentTransform
も適用してやらないといけない。
そのために、次のようにアンカー自体のtransform(originFromAnchorTransform
)を適用するエンティティと、平面の形状を持つ(anchorFromExtentTransform
を適用する)エンティティにわける実装にした:
if let entity = entityMap[anchor.id] {
...
} else {
// Add a new entity to represent this plane.
let entity = Entity()
let material = UnlitMaterial(color: anchor.classification.color)
let planeEntity = ModelEntity(mesh: .generatePlane(width: anchor.geometry.extent.width, height: anchor.geometry.extent.height), materials: [material])
planeEntity.transform = Transform(matrix: anchor.geometry.extent.anchorFromExtentTransform)
...
entity.addChild(planeEntity)
entityMap[anchor.id] = entity
rootEntity.addChild(entity)
}
entityMap[anchor.id]?.transform = Transform(matrix: anchor.originFromAnchorTransform)
これで正しく平面が可視化されるようになった。
サンプル
サンプルコードをGitHubで公開しています。Xcode 15.1 beta 3 で動作確認済み。要実機。
(参考になったらスター、記事へのLikeいただけると嬉しいです)
Discussion