swift 如何跟踪节点顶点的二维坐标?

js4nwp54  于 2023-01-16  发布在  Swift
关注(0)|答案(2)|浏览(154)

我有一个简单的平面节点来跟踪一个面。

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
}

我希望能够跟踪平面所有四个角的坐标,我希望得到投影在屏幕上的二维坐标。

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)))
    }
}

Above跟踪平面的中心位置,但如何跟踪四个角的坐标?
我尝试使用平面的宽度和高度,使用下面的公式来计算到中心坐标的距离,但是我无法获得合适的宽度和高度,我可以使用我为中心坐标获得的屏幕位置。

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
    }
}
0md85ypi

0md85ypi1#

一个解决方案可能是在你想要跟踪的每个角或边上添加i.Ex. invisible childNodes(没有几何体)。然后在你喜欢的任何时候获取每个nodespresentation.worldPosition,并使用上面的函数将它们转换到2D空间。

jjhzyzn0

jjhzyzn02#

隐形跟踪球

尝试以下解决方案。尽管这是一个macOS项目,但您可以在您的ARKit项目中实现此想法。而且,此技术非常容易理解。

import SceneKit

class ViewController: NSViewController {
    
    var sceneView: SCNView? = nil
    let planeNode = SCNNode()
    var vertices: [SCNVector3] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView = self.view as? SCNView
        sceneView?.scene = SCNScene()
        sceneView?.delegate = self
        sceneView?.backgroundColor = .black
        sceneView?.autoenablesDefaultLighting = true
        
        // Plane
        let plane = SCNPlane(width: 0.5, height: 0.5)
        planeNode.name = "planar"
        planeNode.geometry = plane
        planeNode.geometry?.firstMaterial?.isDoubleSided = true
        sceneView?.scene?.rootNode.addChildNode(planeNode)
        planeNode.runAction(SCNAction.move(to: SCNVector3(0.2, 0.2,-0.2), 
                                     duration: 2))            
        self.trackingSpheres()
    }
}

委托的方法(在本例中为renderer(_:didUpdate:for:)示例方法)。

extension ViewController: SCNSceneRendererDelegate {
    
    func renderer(_ renderer: SCNSceneRenderer, 
           updateAtTime time: TimeInterval) {
        
        if let spheres = sceneView?.scene?.rootNode.childNodes[0].childNodes {
            
            for (_, sphere) in spheres.enumerated() {
                
                let truncateX = String(format: "%.2f", sphere.worldPosition.x)
                let truncateY = String(format: "%.2f", sphere.worldPosition.y)
                let truncateZ = String(format: "%.2f", sphere.worldPosition.z)
                
                print("\(sphere.name!):", truncateX, truncateY, truncateZ)
            }
        }
    }
}

一种创建四个不可见的微小跟踪球体的方法。

extension ViewController { 
  
    fileprivate func trackingSpheres() { 
        // retrieving a plane node from scene
        if let node = sceneView?.scene?.rootNode.childNode(
                                                  withName: "planar",
                                               recursively: true) {
            let left = node.boundingBox.min.x
            let right = node.boundingBox.max.x
            let lower = node.boundingBox.min.y
            let upper = node.boundingBox.max.y
            let south = node.boundingBox.min.z                
            // Counter clock-wise
            let ll = SCNVector3(x: left, y: lower, z: south)
            let lr = SCNVector3(x: right, y: lower, z: south)
            let ur = SCNVector3(x: right, y: upper, z: south)
            let ul = SCNVector3(x: left, y: upper, z: south)
            
            self.vertices += [ll, lr, ur, ul]
                        
            for i in 1...4 {                    
                let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.01))
                
                sphereNode.position = SCNVector3(vertices[i-1].x,
                                                 vertices[i-1].y,
                                                 vertices[i-1].z) 
                sphereNode.name = "sphere\(i)"
                sphereNode.opacity = 0.0         // 100% transparent

                sphereNode.geometry?.firstMaterial?.diffuse.contents = 
                                                               NSColor.red
                node.addChildNode(sphereNode)
            }
        }
    }
}

使用vertex semantics分解模型

这个技术比第一个要难得多,有点像逆向工程。

extension SCNGeometry {

    func getVerticesPositions() -> [SCNVector3] {

        let sources = self.sources(for: .vertex)
        guard let source = sources.first else { return [] }

        let stride = source.dataStride / source.bytesPerComponent
        let offset = source.dataOffset / source.bytesPerComponent
        let vectorCount = source.vectorCount

        return source.data.withUnsafeBytes { 
                         (pointer: UnsafePointer<Float>) -> [SCNVector3] in
            
            var vertices: [SCNVector3] = []
            
            for index in 0 ... (vectorCount - 1) {
                
                let bytes = index * stride + offset
                
                let x = pointer[bytes + 0]
                let y = pointer[bytes + 1]
                let z = pointer[bytes + 2]
                
                vertices.append(SCNVector3(x, y, z))
            }
            return vertices
        }
    }
}

让我们看看如何使用它。

var vertices: [SCNVector3] = []

fileprivate func trackingSpheres() {
    
    if let node = sceneView?.scene?.rootNode.childNode(withName: "planar",
                                                    recursively: true) {

        vertices = (node.geometry?.getVerticesPositions())!
    }
    print("vertex1 :", vertices[0])
    print("vertex2 :", vertices[1])
    print("vertex3 :", vertices[2])
    print("vertex4 :", vertices[3])
}

相关问题