ARKit如何获取ARReferenceImage与水平ARPlane交点以放置3D角色
解决ARKit图像识别后在前方平面放置3D角色的问题
嘿,我来帮你搞定这个让3D角色稳稳站在图像前方平面上的问题!你现在遇到的悬浮问题,主要是因为之前的代码没有正确检测ARKit识别到的水平平面,而是直接把Y轴设为0,忽略了实际平面的高度。下面是具体的解决方案:
核心思路
我们需要结合图像锚点的位置和水平平面检测,从图像正前方的位置向下发射射线,找到平面的交点,然后把角色放在这个交点上,这样角色就会稳稳站在平面上了。
具体步骤&代码实现
1. 确保开启水平平面检测
首先在你的ARSession配置里,一定要开启水平平面检测,不然ARKit不会识别平面:
let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = .horizontal // 开启水平平面检测 configuration.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) sceneView.session.run(configuration)
2. 在图像识别回调中处理角色放置
在renderer(_:didAdd:for:)方法里,我们拿到图像锚点后,计算正前方位置,再做射线检测找平面交点:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let imageAnchor = anchor as? ARImageAnchor else { return } // 1. 获取图像锚点的世界位置和正前方方向 let imageTransform = imageAnchor.transform let imagePosition = SCNVector3(imageTransform.columns.3.x, imageTransform.columns.3.y, imageTransform.columns.3.z) // 图像的正前方向量(适配SceneKit坐标系,取反ARKit的z轴方向) let forwardVector = SCNVector3(-imageTransform.columns.2.x, -imageTransform.columns.2.y, -imageTransform.columns.2.z) // 2. 设置角色离图像的距离(比如0.5米,可根据需求调整) let distanceFromImage: Float = 0.5 let targetForwardPosition = imagePosition + forwardVector * distanceFromImage // 3. 从目标位置的上方向下发射射线,覆盖常见平面高度范围 let startRayPoint = SIMD3<Float>(targetForwardPosition.x, targetForwardPosition.y + 1.0, targetForwardPosition.z) let endRayPoint = SIMD3<Float>(targetForwardPosition.x, targetForwardPosition.y - 1.0, targetForwardPosition.z) // 4. 使用ARKit的射线查询找平面交点(iOS 13+推荐,更可靠) if let raycastQuery = sceneView.raycastQuery(from: startRayPoint, to: endRayPoint, allowing: .existingPlaneGeometry, alignment: .horizontal) { let raycastResults = sceneView.session.raycast(raycastQuery) if let closestResult = raycastResults.first { // 5. 获取平面交点的世界坐标,放置角色 let planePosition = SCNVector3(closestResult.worldTransform.columns.3.x, closestResult.worldTransform.columns.3.y, closestResult.worldTransform.columns.3.z) overlayNode.position = planePosition // 可选:让角色和图像朝向一致 overlayNode.eulerAngles.y = node.eulerAngles.y sceneView.scene.rootNode.addChildNode(overlayNode) } } }
3. 兼容旧版本ARKit(iOS 12及以下)
如果你的项目需要兼容iOS 12及以下,可以用旧的hitTest方法替代射线查询:
// 在步骤3之后替换为这段代码 let screenPoint = sceneView.projectPoint(targetForwardPosition) if let planeResult = sceneView.hitTest(screenPoint, types: .existingPlaneUsingExtent).first { let planePosition = SCNVector3(planeResult.worldTransform.columns.3.x, planeResult.worldTransform.columns.3.y, planeResult.worldTransform.columns.3.z) overlayNode.position = planePosition overlayNode.eulerAngles.y = node.eulerAngles.y sceneView.scene.rootNode.addChildNode(overlayNode) }
为什么之前的代码会导致悬浮?
你之前用sceneView.scene.rootNode.hitTestWithSegment是检测SceneKit场景中的节点,而不是ARKit识别到的真实平面;而且直接把Y轴设为0,忽略了实际平面的高度(比如桌子的高度不是0),所以角色会悬浮在空中。
内容的提问来源于stack exchange,提问作者Daniil
相关产品推荐
相关产品推荐

