我正在尝试理解和使用ARKit。但有一件事我无法完全理解。
苹果关于ARAnchor的说法:
一个真实世界的位置和方向,可用于在AR场景中放置物体。
但这并不足够。所以我的问题是:
ARAnchor
到底是什么?- 锚点和特征点之间有什么区别?
ARAnchor
只是特征点的一部分吗?- ARKit如何确定其锚点?
我正在尝试理解和使用ARKit。但有一件事我无法完全理解。
苹果关于ARAnchor的说法:
一个真实世界的位置和方向,可用于在AR场景中放置物体。
但这并不足够。所以我的问题是:
ARAnchor
到底是什么?ARAnchor
只是特征点的一部分吗?更新日期:2023年10月15日。
ARAnchor
是一个不可见的可跟踪对象,它在锚点位置上保存了一个3D模型。将ARAnchor
视为您的模型的父变换节点
,您可以像SceneKit或RealityKit中的任何其他节点一样对其进行平移、旋转和缩放。每个3D模型都有一个枢轴点,对吧?所以,这个枢轴点必须与AR应用中的ARAnchor
的位置匹配。
如果您在ARKit或ARCore应用中不使用锚点(但在RealityKit iOS中,不使用锚点是不可能的,因为它们是场景的必要组成部分),您的3D模型可能会偏离其放置的位置,这将极大地影响应用的逼真度和用户体验。因此,锚点是任何AR场景中至关重要的元素。
根据ARKit 2017文档:
ARAnchor
是一个真实世界的位置和方向,可用于在AR场景中放置物体。将锚点添加到会话中可以帮助ARKit优化围绕该锚点的区域的世界跟踪精度,使虚拟物体相对于真实世界保持固定位置。如果虚拟物体移动,应从旧位置移除相应的锚点,并在新位置添加一个锚点。
ARAnchor
是ARKit中其他10种锚点类型的父类,因此所有这些子类都继承自ARAnchor
。通常情况下,您不直接使用ARAnchor
。我还必须说ARAnchor
和Feature Points
没有任何共同之处。Feature Points
是用于跟踪和调试的特殊视觉元素。
ARAnchor
不会自动跟踪真实世界的目标。当您需要自动化时,您必须使用renderer()
或session()
委托方法,这些方法可以在您遵循ARSCNViewDelegate
或ARSessionDelegate
协议的情况下实现。
ARPlaneAnchor
。因此,如果您想在场景中看到锚点,您必须使用三个细长的SCNCylinder
原始体来“可视化”它。每个圆柱体的颜色代表一个特定的轴:RGB即XYZ。
ARPlaneAnchor
planeDetection
实例属性为ON
,ARKit能够将ARPlaneAnchors添加到正在运行的会话的锚点集合中。有时激活planeDetection
会显著增加场景理解阶段所需的时间。ARImageAnchor(符合ARTrackable协议)
detectionImages
实例属性。在ARKit 2.0中,您可以完全跟踪多达25个图像,在ARKit 3.0 / 4.0中,分别可以跟踪多达100个图像。但是,在这两种情况下,同时只能跟踪不超过4个图像。然而,承诺在ARKit 5.0 / 6.0中可以同时检测和跟踪多达100个图像(但尚未实现)。ARBodyAnchor(符合ARTrackable
协议)
ARBodyTrackingConfig()
运行会话来启用身体跟踪。您将在真实表演者的骨架的根关节或者说在被跟踪角色的骨盆位置处获得ARBodyAnchor。ARFaceAnchor(符合ARTrackable
协议)
ARFaceAnchor
。当检测到面部时,Face Anchor将附在鼻子稍后的位置,位于面部的中心。在ARKit 2.0中,您只能跟踪一个面部,在ARKit 3.0及更高版本中,可以同时跟踪多达3个面部。然而,跟踪的面部数量取决于TrueDepth传感器和处理器版本的存在:配备TrueDepth摄像头的设备可以跟踪多达3个面部,配备A12+芯片组但没有TrueDepth摄像头的设备也可以跟踪多达3个面部。ARObjectAnchor
detectionObjects
属性指定ARReferenceObject
实例。AREnvironmentProbeAnchor
ARParticipantAnchor
isCollaborationEnabled
属性中使用true
值。然后导入MultipeerConnectivity
框架。ARMeshAnchor
30-40个锚点
甚至更多。这是因为每个分类对象(墙壁、椅子、门或桌子)都有自己的个人锚点。每个ARMeshAnchor存储有关相应顶点、八种分类情况之一、其面和顶点法线的数据。ARGeoAnchor(符合ARTrackable
协议)
ARAppClipCodeAnchor(符合ARTrackable
协议)
点击测试方法
ARHitTestResult
类及其对应的用于ARSCNView和ARSKView的点击测试方法将被完全弃用,所以你必须习惯使用射线投射。射线投射方法
特征点
AR相机的变换
任意的世界位置
world anchor
,就像在RealityKit中找到的AnchorEntity(.world(transform: mtx))
一样。renderer(_:didAdd:for:)
中使用ARPlaneAnchor。func renderer(_ renderer: SCNSceneRenderer,
didAdd node: SCNNode,
for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor
else { return }
let grid = Grid(anchor: planeAnchor)
node.addChildNode(grid)
}
ARKit 2023的API已经在visionOS上进行了重大修改。主要创新是创建了三个协议(组合优于继承,记住吗?):Anchor,它的子类TrackableAnchor和DataProvider。现在有六种类型的visionOS ARKit锚点,它们是结构体而不是类。每种锚点类型都有一个对应的提供者对象,例如:ImageAnchor有一个ImageTrackingProvider
,HandAnchor有一个HandTrackingProvider
,等等。
@available(visionOS 1.0, *)
public struct WorldAnchor : TrackableAnchor, @unchecked Sendable
@available(visionOS 1.0, *)
// The position and orientation of Apple Vision Pro headset.
public struct DeviceAnchor : TrackableAnchor, @unchecked Sendable
@available(visionOS 1.0, *)
public struct PlaneAnchor : Anchor, @unchecked Sendable
@available(visionOS 1.0, *)
public struct HandAnchor : TrackableAnchor, @unchecked Sendable
@available(visionOS 1.0, *)
public struct MeshAnchor : Anchor, @unchecked Sendable
@available(visionOS 1.0, *)
public struct ImageAnchor : TrackableAnchor, @unchecked Sendable
AnchorEntity是RealityKit中的关键。根据2019年的RealityKit文档:
AnchorEntity
是将虚拟内容与AR会话中的真实对象连接的锚点。
在WWDC'19上宣布了RealityKit框架和Reality Composer应用程序。它们有一个名为AnchorEntity
的新类。您可以将AnchorEntity用作任何实体层次结构的根点,并且必须将其添加到场景锚点集合中。AnchorEntity会自动跟踪真实世界目标。在RealityKit和Reality Composer中,AnchorEntity
位于层次结构的顶部。这个锚点能够容纳数百个模型,在这种情况下比为每个模型使用100个个人锚点更稳定。
让我们看看代码中的示例:
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
let modelAnchor = try! Experience.loadModel()
arView.scene.anchors.append(modelAnchor)
return arView
}
AnchorEntity
有三个组件:
要了解
ARAnchor
和AnchorEntity
之间的区别,请查看此帖子。
以下是iOS上RealityKit中可用的九个AnchorEntity案例:
// Fixed position in the AR scene
AnchorEntity(.world(transform: mtx))
// For body tracking (a.k.a. Motion Capture)
AnchorEntity(.body)
// Pinned to the tracking camera
AnchorEntity(.camera)
// For face tracking (Selfie Camera config)
AnchorEntity(.face)
// For image tracking config
AnchorEntity(.image(group: "GroupName", name: "forModel"))
// For object tracking config
AnchorEntity(.object(group: "GroupName", name: "forObject"))
// For plane detection with surface classification
AnchorEntity(.plane([.any], classification: [.seat], minimumBounds: [1, 1]))
// When you use ray-casting
AnchorEntity(raycastResult: myRaycastResult)
// When you use ARAnchor with a given identifier
AnchorEntity(.anchor(identifier: uuid))
// Creates anchor entity on a basis of ARAnchor
AnchorEntity(anchor: arAnchor)
// Fixed world position in VR scene
AnchorEntity(.world(transform: mtx))
// Camera transform
AnchorEntity(.camera)
// User's hand anchor, taking into account chirality
AnchorEntity(.hand(.right, location: .thumbTip))
// Head anchor
AnchorEntity(.head)
手性
一词意味着相对于右侧和左侧缺乏对称性。在RealityKit中,手性是指使用三种情况:.either
、.left
和.right
。此外,还有五种情况描述锚点的位置:.wrist
、.palm
、.thumbTip
、.indexFingerTip
、.aboveHand
。
ARAnchor
的子类来满足AnchorEntity
的需求:var anchor = AnchorEntity()
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let faceAnchor = anchors.first as? ARFaceAnchor
else { return }
arView.session.add(anchor: faceAnchor) // ARKit Session
self.anchor = AnchorEntity(anchor: faceAnchor)
anchor.addChild(model)
arView.scene.anchors.append(self.anchor) // RealityKit Scene
}
目前(2023年6月),iOS Reality Composer只有4种类型的锚点实体:
// 1a
AnchorEntity(plane: .horizontal)
// 1b
AnchorEntity(plane: .vertical)
// 2
AnchorEntity(.image(group: "GroupName", name: "forModel"))
// 3
AnchorEntity(.face)
// 4
AnchorEntity(.object(group: "GroupName", name: "forObject"))
当然,我应该说几句关于预备锚点的话。对于那些喜欢使用Python脚本创建USDZ模型的人来说,有3种预备锚点类型(2022年7月)- 分别是plane
、image
和face
预备锚点。看看这段代码片段,了解如何以Pythonic的方式实现模式。
def Cube "ImageAnchoredBox"(prepend apiSchemas = ["Preliminary_AnchoringAPI"])
{
uniform token preliminary:anchoring:type = "image"
rel preliminary: imageAnchoring:referenceImage = <ImageReference>
def Preliminary_ReferenceImage "ImageReference"
{
uniform asset image = @somePicture.jpg@
uniform double physicalWidth = 45
}
}
这是一个在RealityKit中可视化锚点的示例(Mac版本)。
import AppKit
import RealityKit
class ViewController: NSViewController {
@IBOutlet var arView: ARView!
var model = Entity()
let anchor = AnchorEntity()
fileprivate func visualAnchor() -> Entity {
let colors: [SimpleMaterial.Color] = [.red, .green, .blue]
for index in 0...2 {
let box: MeshResource = .generateBox(size: [0.20, 0.005, 0.005])
let material = UnlitMaterial(color: colors[index])
let entity = ModelEntity(mesh: box, materials: [material])
if index == 0 {
entity.position.x += 0.1
} else if index == 1 {
entity.transform = Transform(pitch: 0, yaw: 0, roll: .pi/2)
entity.position.y += 0.1
} else if index == 2 {
entity.transform = Transform(pitch: 0, yaw: -.pi/2, roll: 0)
entity.position.z += 0.1
}
model.scale *= 1.5
self.model.addChild(entity)
}
return self.model
}
override func awakeFromNib() {
anchor.addChild(self.visualAnchor())
arView.scene.addAnchor(anchor)
}
}
在我的帖子末尾,我想谈谈在ARCore 1.40+中使用的五种锚点类型。谷歌的官方文档对锚点的定义如下:“ArAnchor描述了现实世界中的固定位置和方向”。ARCore的锚点在许多方面与ARKit的锚点类似。
让我们来看看ArAnchors的类型:
本地锚点
云锚点
地理空间锚点
地形锚点
屋顶锚点
fun configureSession(session: Session) {
session.configure(
session.config.apply {
geospatialMode = Config.GeospatialMode.ENABLED
}
)
}
val earth = session?.earth ?: return
if (earth.trackingState != TrackingState.TRACKING) { return }
earthAnchor?.detach()
val altitude = earth.cameraGeospatialPose.altitude - 1
val qx = 0f; val qy = 0f; val qz = 0f; val qw = 1f
earthAnchor = earth.createAnchor(latLng.latitude,
latLng.longitude,
altitude,
qx, qy, qz, qw)
streetscapeGeometryMode = Config.StreetscapeGeometryMode.ENABLED
val streetscapeGeo = session.getAllTrackables(StreetscapeGeometry::class.java)
streetscapeGeometryRenderer.render(render, streetscapeGeo)
val centerHits = frame.hitTest(centerCoords[0], centerCoords[1])
val hit = centerHits.firstOrNull {
val trackable = it.trackable
trackable is StreetscapeGeometry &&
trackable.type == StreetscapeGeometry.Type.BUILDING
} ?: return
val transformedPose = ObjectPlacementHelper.createStarPose(hit.hitPose)
val anchor = hit.trackable.createAnchor(transformedPose)
starAnchors.add(anchor)
val earth = session?.earth ?: return
val geospatialPose = earth.getGeospatialPose(transformedPose)
earth.resolveAnchorOnRooftopAsync(geospatialPose.latitude,
geospatialPose.longitude,
0.0,
transformedPose.qx(),
transformedPose.qy(),
transformedPose.qz(),
transformedPose.qw() ) { anchor, state ->
if (!state.isError) {
balloonAnchors.add(anchor)
}
}
raywenderlich's
的ARKit书籍。 - Andy Jazz