You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:59:18