ARKitで動き出すポスターを作る


ポスターやカードをカメラで写すとARが浮かび上がってくるタイプのARが流行っています。手軽でできて分かりやすいARです。この機能をARKitで実装する方法を紹介していきます。

以下みたいなイメージ。


https://www.youtube.com/watch?v=mg-RDMab6VQ

[Swift/ARKit2] 動くポスターを1時間でつくろうという記事の実装部分の解説です。

ポスター画像と再生用の動画を用意する

 画像マーカーとなるポスター画像ファイルと、マーカーを読み取った際に再生する動画ファイルを用意しましょう。それぞれjpgファイルとmp4ファイルだと間違いないと思います。また、2Dのポスターが突然動き出すように表現するためには、それぞれ縦横比を統一した方が良いでしょう。

ポスター画像をARImageResourceに登録する

 ARKitで作るAR名刺と同じ容量で、ポスターが画像をARImageResourceに登録しましょう。

ポスター動画をプロジェクトに追加する

 ポスター動画(例ではmovie.mp4というファイル)をXcodeのプロジェクトに対してドラッグアンドドロップします。

画像を読み取ったら反応するようにする

まずはsceneViewをARSCNViewDelegateに準拠させます。また、AR Resourcesの画像をtrackingImagesとしてARImageTrackingConfigurationにセットしときます。

override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.delegate = self
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let configuration = ARImageTrackingConfiguration()

    if let trackedImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: Bundle.main) {
        configuration.trackingImages = trackedImages
    }
    sceneView.session.run(configuration)
}

 以下のようにrenderer(didAdd node:)のメソッドを書き、ARImageAnchorの発見を検知します。

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

SKVideoNodeとSKSceneで動画のシーンを作成する

 次にARKit(正確にはSceneKit)で動画を再生する方法を解説します。動画自体は2Dなので、今回SpriteKitを使って動画を再生します。SpriteKitで作られたSKSceneをSceneKitで再生していきます。

 予め用意してXcodeにドラッグアンドドロップしていたmovie.mp4を呼び出して動画を再生します。

guard let imageAnchor = anchor as? ARImageAnchor else {
    return
}
guard let fileUrlString = Bundle.main.path(forResource: "movie", ofType: "mp4") else {
    return
}
let videoItem = AVPlayerItem(url: URL(fileURLWithPath: fileUrlString))
let player = AVPlayer(playerItem: videoItem)
player.play()

次に、SKSceneを作成し、その中にSKVideoNodeを追加します。

let size = CGSize(width: 480, height: 360)
let videoScene = SKScene(size: size)

let videoNode = SKVideoNode(avPlayer: player)
videoNode.position = CGPoint(x: size.width / 2, y: size.height / 2)
videoNode.yScale = -1.0

videoScene.addChild(videoNode)

SCNPlaneのマテリアルとして動画のシーンをセットする

 最後にポスターと同じサイズの平面のジオメトリを持つSCNNodeを作成し、そのマテリアルとして、動画を再生するSKSceneを渡すことで、動画を再生する平面のノードを作ることができます。最後にその平面をImageAnchorの位置にあるnodeに追加して完成です。

let plane = SCNPlane(
    width: imageAnchor.referenceImage.physicalSize.width,
    height: imageAnchor.referenceImage.physicalSize.height
)
plane.firstMaterial?.diffuse.contents = videoScene

let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -Float.pi / 2
node.addChildNode(planeNode)

画像検知後の一連のコードは以下です。

extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let imageAnchor = anchor as? ARImageAnchor,
            let fileUrlString = Bundle.main.path(forResource: "black", ofType: "mp4") else {
                return
        }
        let videoItem = AVPlayerItem(url: URL(fileURLWithPath: fileUrlString))
        let player = AVPlayer(playerItem: videoItem)
        player.play()

        let size = CGSize(width: 480, height: 360)
        let videoScene = SKScene(size: size)

        let videoNode = SKVideoNode(avPlayer: player)
        videoNode.position = CGPoint(x: size.width / 2, y: size.height / 2)
        videoNode.yScale = -1.0

        videoScene.addChild(videoNode)

        let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width,
                             height: imageAnchor.referenceImage.physicalSize.height)
        plane.firstMaterial?.diffuse.contents = videoScene
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = -Float.pi / 2
        node.addChildNode(planeNode)
    }
}

以上です。特定のポスター画像を発見すると、その位置に動画を再生することができました。

(動画はイメージ Using ARKit To Display Video In Augmented Realityから引用)

関連記事

https://qiita.com/IZUMIRU/items/3c36f7730aff5e6305c5
https://medium.com/quick-code/using-arkit-to-display-video-in-augmented-reality-3a2e4c1418ad