iOS
SceneKit
Swift
ARKit
ios11
ARKitDay 20

ARKitで空間上に写真を置く

ARKitを使ってiPhoneのカメラロールに入っている写真を空間上に置いてみます!

IMB_1dDqIg.GIF

今回サンプルで作ってみたViewControllerはgithubに置きましたので、全体はそちらをご確認ください。

手順

空間上に写真を置くためには以下のステップがあります。

  • 1. UIImagePickerControllerを使ってカメラロールの写真を取得する
  • 2. 写真のUIImageを持ったSCNNodeを作る
  • 3. そのnodeをsceneViewに貼る

それぞれ順番に解説していきます。

UIImagePickerControllerを使ってカメラロールの写真を取得する

まずは、UIImagePickerControllerを作ってpresentします。

    private func showUIImagePicker() {
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
            let pickerView = UIImagePickerController()
            pickerView.sourceType = .photoLibrary
            pickerView.delegate = self
            pickerView.modalPresentationStyle = .overFullScreen
            self.present(pickerView, animated: true, completion: nil)
        }
    }

pickerViewでのアクションを受け取るためにViewConteollerはdelegateに準拠させます。

pickerView.delegate = self
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            setImageToScene(image: image)
        }
        picker.dismiss(animated: true, completion: nil)
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
}

これで、カメラロールのUIImageを取得できましたので、あらかじめ作っておいた

func setImageToScene(image: UIImage)

メソッドでSceneViewに貼っていきます。

写真のUIImageを持ったSCNNodeを作る

とその前に、SCNNodeを作るメソッドを用意しときましょう。

    private func createPhotoNode(_ image: UIImage, position: SCNVector3) -> SCNNode {
        let node = SCNNode()
        let scale: CGFloat = 0.3
        let geometry = SCNBox(width: image.size.width * scale / image.size.height,
                              height: scale,
                              length: 0.00000001,
                              chamferRadius: 0.0)
        geometry.firstMaterial?.diffuse.contents = image
        node.geometry = geometry
        node.position = position
        return node
    }

ARKit登場以前からあるSceneKitには様々なSCNNodeが用意されていますが、今回は限りなく奥行きが薄いSCNBoxを作って写真として空間に貼っていくことにしました。

ポイントは、

geometry.firstMaterial?.diffuse.contents = image

で、これやらないとただの白い面ができるだけになるので、要注意!

(ぶっちゃけですが、ここであるscaleはいい感じになるようにチューニングしただけです。笑)

そのnodeをsceneViewに貼る

最後に、sceneViewにnodeを追加していきます。

先ほど作った

func createPhotoNode(_ image: UIImage, position: SCNVector3) -> SCNNode

メソッドを呼んでnodeを作っていますが、その引数に渡しているpositionによって写真がどこに貼られるかが決まります。

今回は、https://dev.classmethod.jp/smartphone/iphone/ios11-arkit-pointofview/ を参考にさせてもらって、カメラの中央、カメラから50cm先に置かれるようにしています。

    private func setImageToScene(image: UIImage) {
        if let camera = sceneView.pointOfView {
            // カメラから見て50cm先の座標
            let position = SCNVector3(x: 0, y: 0, z: -0.5) 
            // 世界座標系に変換
            let convertedPosition = camera.convertPosition(position, to: nil)
            let node = createPhotoNode(image, position: convertedPosition)
            self.sceneView.scene.rootNode.addChildNode(node)
        }
    }

以上で、カメラロールで選択した写真をカメラの中央部分に面として貼ることができました!

IMB_1dDqIg.GIF

参考

関連APP

github