如何追踪AR平面节点四个顶点的屏幕2D坐标?
如何获取AR人脸追踪平面节点的四个顶点屏幕坐标?
现有代码实现
1. 创建人脸追踪平面节点
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { guard anchor is ARFaceAnchor else { return nil } let plane = SCNPlane(width: 0.1, height: 0.2) let planeNode = SCNNode(geometry: plane) planeNode.geometry?.firstMaterial?.fillMode = .lines planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue planeNode.name = "Plane Node" return planeNode }
2. 追踪平面中心屏幕坐标
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } node.enumerateChildNodes { childNode, _ in guard childNode.name == "Plane Node" else { return } let worldPosition = childNode.worldPosition let screenPosition = renderer.projectPoint(worldPosition) print(CGPoint(x: Int(screenPosition.x), y: Int(screenPosition.y))) } }
3. 尝试推导顶点坐标的扩展(未成功)
extension SCNNode { var width: Float { return (boundingBox.max.x - boundingBox.min.x) * scale.x } var height: Float { return (boundingBox.max.y - boundingBox.min.y) * scale.y } }
问题
目前能成功获取平面中心的屏幕坐标,但无法正确计算平面四个顶点的2D屏幕坐标。尝试通过扩展计算节点宽高,再从中心坐标推导顶点坐标的方法,得到的宽高值无法适配屏幕坐标系,结果不准确。
解决方案
正确思路是先计算平面四个顶点在3D世界空间中的位置,再通过AR场景渲染器的投影方法转换为屏幕坐标,具体实现如下:
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } node.enumerateChildNodes { childNode, stop in guard let planeNode = childNode as? SCNNode, let plane = planeNode.geometry as? SCNPlane, childNode.name == "Plane Node" else { return } // 获取平面原始半尺寸 let planeHalfWidth = plane.width / 2 let planeHalfHeight = plane.height / 2 // 定义平面四个顶点在本地坐标系中的位置(以平面中心为原点) let localCorners: [SCNVector3] = [ SCNVector3(-planeHalfWidth, -planeHalfHeight, 0), // 左下顶点 SCNVector3(planeHalfWidth, -planeHalfHeight, 0), // 右下顶点 SCNVector3(planeHalfWidth, planeHalfHeight, 0), // 右上顶点 SCNVector3(-planeHalfWidth, planeHalfHeight, 0) // 左上顶点 ] // 转换为世界坐标并投影到屏幕 localCorners.enumerated().forEach { index, localCorner in let worldCorner = planeNode.convertPosition(localCorner, to: nil) let screenVector = renderer.projectPoint(worldCorner) let screenPoint = CGPoint(x: Int(screenVector.x), y: Int(screenVector.y)) print("顶点\(index+1)屏幕坐标:\(screenPoint)") } } }
原理说明
之前方法失效的原因:
boundingBox计算的是节点的轴对齐包围盒,当平面节点伴随人脸旋转时,包围盒宽高无法反映平面在世界空间的实际尺寸- 直接从中心推导顶点坐标未考虑节点旋转、缩放以及相机透视投影关系,导致屏幕坐标偏差
通过本地坐标转世界坐标再投影的方式,会自动处理节点的旋转、缩放变换以及相机透视投影,确保屏幕坐标准确对应平面的四个顶点位置。
内容的提问来源于stack exchange,提问作者Kevvv
相关产品推荐
相关产品推荐

