【问题标题】:How to calculate quadrangle for visible part of vertical plane?如何计算垂直平面可见部分的四边形?
【发布时间】:2021-03-11 18:07:40
【问题描述】:

我的目标是计算固定到某个ARPlaneAnchor 的垂直平面的“可见”部分,并用四边形表示,如下图所示:

我目前的方法是基于少数命中测试,不幸的是,这似乎没有给我令人满意的结果。

首先,当我检测到ARPlaneAnchor 时,我会在其主节点中添加一个不可见的大SCNNode。

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else { return }

    let planeNode = SCNNode(geometry: SCNPlane(width: 100.0, height: 100.0))
    planeNode.name = "infinite_plane"

    planeNode.position = SCNVector3(planeAnchor.center.x, planeAnchor.center.y, planeAnchor.center.z)
    planeNode.eulerAngles = SCNVector3(-0.5 * .pi, 0, 0)

    planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.clear

    node.addChildNode(planeNode)

    node.enumerateChildNodes { childNode, _ in
        childNode.removeFromParentNode()
    }
}

然后,当需要计算垂直平面的可见部分时(时间无关紧要,可以是在某些屏幕触摸之后或每次渲染新帧时),我会取屏幕的中心并执行一些命中测试。

let center = CGPoint(x: sceneView.bounds.midX, y: sceneView.bounds.midY)
let hitTestResults = sceneView.hitTest(center, types: [.existingPlaneUsingGeometry, .estimatedVerticalPlane])
if let hitTestResult = hitTestResults.first,
   let hitAnchor = hitTestResult.anchor,
   let hitNode = sceneView.node(for: hitAnchor) {
    
    let width = sceneView.bounds.width
    let height = sceneView.bounds.height
    // A B
    // C D
    guard let aPosition = sceneView.hitTest(CGPoint(x: 0, y: 0)).first?.worldCoordinates,
          let bPosition = sceneView.hitTest(CGPoint(x: width, y: 0)).first?.worldCoordinates,
          let cPosition = sceneView.hitTest(CGPoint(x: 0, y: height)).first?.worldCoordinates,
          let dPosition = sceneView.hitTest(CGPoint(x: width, y: height)).first?.worldCoordinates else {
        return
    }
    let geometry = SCNGeometry.polygon(vertices: [aPosition, bPosition, dPosition, cPosition])
    let node = SCNNode(geometry: geometry)
    node.geometry?.firstMaterial?.diffuse.contents = UIColor.green
    node.geometry?.firstMaterial?.isDoubleSided = true
    sceneView.scene.rootNode.addChildNode(node)
}

extension SCNGeometry {

    static func polygon(vertices: [SCNVector3]) -> SCNGeometry {
        let source = SCNGeometrySource(vertices: vertices)

        var indices = vertices.indices.map { UInt32($0) }
        indices.insert(UInt32(vertices.count), at: 0)

        let data = Data(bytes: indices, count: indices.count * MemoryLayout<Int32>.size)

        let element = SCNGeometryElement(
            data: data,
            primitiveType: .polygon,
            primitiveCount: 1,
            bytesPerIndex: MemoryLayout<Int32>.size
        )

        return SCNGeometry(sources: [source], elements: [element])
    }
}

然后我停了下来。绘制的多边形似乎并不代表垂直平面的可见部分。

问题是,我的估计哪里错了,我应该如何正确检测垂直平面的“可见”部分?

【问题讨论】:

  • 找到解决方案成功了吗?

标签: swift augmented-reality scenekit arkit


【解决方案1】:

试试这个方法:

extension ViewController: ARSCNViewDelegate {
    
    func renderer(_ renderer: SCNSceneRenderer,
                 didAdd node: SCNNode,
                  for anchor: ARAnchor) {
        
        guard let planeAnchor = anchor as? ARPlaneAnchor
        else { return }
        
        let width = CGFloat(planeAnchor.extent.x)
        let height = CGFloat(planeAnchor.extent.z)
        
        let myPlane = SCNPlane(width: width,
                              height: height)

        myPlane.materials.first?.diffuse.contents = UIColor(white: 0.0, 
                                                            alpha: 0.5)
        
        let planeNode = SCNNode(geometry: myPlane)
        let x = CGFloat(planeAnchor.center.x)
        let y = CGFloat(planeAnchor.center.y)
        let z = CGFloat(planeAnchor.center.z)
        
        planeNode.position = SCNVector3(x, y, z)
        planeNode.eulerAngles.x = -Float.pi / 2
        
        node.addChildNode(planeNode)
    }      
    
    func renderer(_ renderer: SCNSceneRenderer,
              didUpdate node: SCNNode,
                  for anchor: ARAnchor) {
        
        guard let planeAnchor = anchor as? ARPlaneAnchor,
              let planeNode = node.childNodes.first,
              let myPlane = planeNode.geometry as? SCNPlane
        else { return }
        
        let width = CGFloat(planeAnchor.extent.x)
        let height = CGFloat(planeAnchor.extent.z)
        myPlane.width = width
        myPlane.height = height
        
        let x = CGFloat(planeAnchor.center.x)
        let y = CGFloat(planeAnchor.center.y)
        let z = CGFloat(planeAnchor.center.z)
        planeNode.position = SCNVector3(x, y, z)
    }
}

【讨论】:

  • 看起来你只是放置了平面节点而不计算“可见部分”已经存在的平面我正确吗?
猜你喜欢
  • 2013-03-25
  • 2011-01-25
  • 1970-01-01
  • 1970-01-01
  • 2019-04-22
  • 2017-05-12
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多