Вычисление положения 4 вершин повернутого SCNPlane в опыте ARKit - PullRequest
0 голосов
/ 17 апреля 2019

Я работаю над приложением для iOS и в настоящее время пытаюсь получить координаты (относительно мира / rootNode) 4 вершин повернутого SCNNode в AR Experience

Из того, что якак видно из отладки этого беспорядка, ограничивающая коробка повернутого и наклоненного SCNPlane по-прежнему находится на одном уровне с мировым происхождением.

Я пробовал решение в этом потоке.

Самолет, который я пытаюсь обнаружить, помещается прямо поверх изображения, обнаруженного ARKit:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let imageAnchor = anchor as? ARImageAnchor else { return }
    let referenceImage = imageAnchor.referenceImage

    updateQueue.async {

        // Create a plane to visualize the initial position of the detected image.
        let plane = SCNPlane(width: referenceImage.physicalSize.width,
                             height: referenceImage.physicalSize.height)
        let planeNode = SCNNode(geometry: plane)
        planeNode.opacity = 0.25

        /*
         `SCNPlane` is vertically oriented in its local coordinate space, but
         `ARImageAnchor` assumes the image is horizontal in its local space, so
         rotate the plane to match.
         */
        planeNode.eulerAngles.x = -.pi / 2

        /*
         Image anchors are not tracked after initial detection, so create an
         animation that limits the duration for which the plane visualization appears.
         */
        planeNode.runAction(self.imageHighlightAction)

        // This is where I tried to calculate the edge
        let (min, max) = planeNode.boundingBox

        let topLeft = SCNVector3(min.x, max.y, 0)

        let worldTopLeft = planeNode.convertPosition(topLeft, to: self.sceneView.scene.rootNode)

        let world2dTopLeft = self.sceneView.projectPoint(worldTopLeft)


        node.addChildNode(planeNode)
    }
}

Ожидаемый результат - это некая 2D-позиция на экране, которая будетпрямо в верхней части угла в 3D-виде, но я всегда получаю огромные цифры, например x: 8847.291, y: -10651.121

Кто-нибудь нашел решение этой проблемы?

Ответы [ 2 ]

0 голосов
/ 24 апреля 2019

Хорошо, я нашел решение:

Мне нужно вычислить это, выполнив некоторую сложную математику, которую некоторые из вас, вероятно, могут объяснить лучше, чем я.

Рабочая копия (для Swift 4.2):

let imageAnchor: ARImageAnchor! // provided by renderer()
let scale: CGFloat!             // UIDevice.main.scale (device scale)

// returns a transform matrix from a point
let imageTransform: (_ x: CGFloat, _ y: CGFloat) -> float4x4 = {x, y in
    var tf = float4x4(diagonal: float4(repeating: 1))
    tf.columns.3 = float4(x: Float(x), y: 0, z: Float(y), w: 1)
    return tf
}

let imageSize = imageAnchor.referenceImage.physicalSize
let worldTransform = imageAnchor.transform

let topLeft = imageTransform(-imageSize.width / 2, -imageSize.height / 2)
let topRight = imageTransform(imageSize.width / 2, -imageSize.height / 2)
let bottomLeft = imageTransform(-imageSize.width / 2, imageSize.height / 2)
let bottomRight = imageTransform(imageSize.width / 2, imageSize.height / 2)

let transformTopLeft = worldTransform * topLeft
let transformTopRight = worldTransform * topRight
let transformBottomLeft = worldTransform * bottomLeft
let transformBottomRight = worldTransform * bottomRight

let pointTopLeft = self.sceneView.projectPoint(SCNVector3(x: transformTopLeft.columns.3.x,
                                                          y: transformTopLeft.columns.3.y,
                                                          z: transformTopLeft.columns.3.z))

let pointTopRight = self.sceneView.projectPoint(SCNVector3(x: transformTopRight.columns.3.x,
                                                           y: transformTopRight.columns.3.y,
                                                           z: transformTopRight.columns.3.z))

let pointBottomLeft = self.sceneView.projectPoint(SCNVector3(x: transformBottomLeft.columns.3.x,
                                                             y: transformBottomLeft.columns.3.y,
                                                             z: transformBottomLeft.columns.3.z))

let pointBottomRight = self.sceneView.projectPoint(SCNVector3(x: transformBottomRight.columns.3.x,
                                                              y: transformBottomLeft.columns.3.y,
                                                              z: transformBottomLeft.columns.3.z))

// Those are the CGPoints, projected to the screen
let cgTL = CGPoint(x: CGFloat(pointTopLeft.x)*scale, y: extent.height-CGFloat(pointTopLeft.y)*scale)
let cgTR = CGPoint(x: CGFloat(pointTopRight.x)*scale, y: extent.height-CGFloat(pointTopRight.y)*scale)
let cgBL = CGPoint(x: CGFloat(pointBottomLeft.x)*scale, y: extent.height-CGFloat(pointBottomLeft.y)*scale)
let cgBR = CGPoint(x: CGFloat(pointBottomRight.x)*scale, y: extent.height-CGFloat(pointBottomRight.y)*scale)

Это сработало просто отлично. Надеюсь, кто-нибудь сможет объяснить, что я там сделал.

0 голосов
/ 24 апреля 2019

Вы не должны устанавливать позицию z в ​​ноль при преобразовании из локального пространства в мировое пространство. Вы можете использовать ограничивающие рамки min и max значения без их изменения:

let (min, max) = planeNode.boundingBox

// local space to world space
let minWorld = planeNode.convertPosition(min, to: self.sceneView.scene.rootNode)
let maxWorld = planeNode.convertPosition(max, to: self.sceneView.scene.rootNode)

// world space to screen space
let minScreen = self.sceneView.projectPoint(minWorld)
let maxScreen = self.sceneView.projectPoint(maxWorld)

Затем можно использовать координаты x и y minScreen и maxScreen и игнорировать z.

...