7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ARSCNViewでZoomの真似をする

アプリとソースコード公開しています。
ARCamera.
AppStore:https://apps.apple.com/us/app/id1488699740
GitHub:https://github.com/john-rocky/ARCamera
argif.gif

ViewController.swift
@IBOutlet var sceneView: ARSCNView!
var virtualBackgroundNode = SCNNode()

人物を手前に残す(People Occlution)##

ARSessionにPeople Occlutionを設定しないと、ARコンテンツが人物をオーバーラップしてしまいます。

Occlutionなし
スクリーンショット 2020-07-21 0.16.29.png

Occlutionあり
スクリーンショット 2020-07-21 0.17.24.png

ViewController.swift
 let config = ARFaceTrackingConfiguration()                    
      if ARFaceTrackingConfiguration.supportsFrameSemantics(.personSegmentation) {
            config.frameSemantics.insert(.personSegmentation)
      } else {
            presentAlert(NSLocalizedString("この端末/OSではピープルオクルージョンを利用できません", comment: ""))
      }
 sceneView.session.run(config, options: [])

SCNNodeでバーチャル背景をつくる

SCNPlaneをつくり、画像や動画を貼りつけます。
貼り付けるコンテンツはSpriteKitのSKSceneとしてつくります。

動画の場合

let avPlayer = AVPlayer(url: videoUrl)
//画像の縦横比が歪まないように調整しています
var mediaAspectRatio: Double!
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else { return (nil,nil) }
let size = track.naturalSize.applying(track.preferredTransform)
let resolution = (CGSize(width: abs(size.width), height:abs(size.height)),track.preferredTransform)
let width = resolution.0?.width
let height = resolution.0?.height
mediaAspectRatio = Double(width! / height! )
avPlayer.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none;
NotificationCenter.default.addObserver(self,
                                       selector: #selector(ViewController.didPlayToEnd),
                                       name: NSNotification.Name("AVPlayerItemDidPlayToEndTimeNotification"),
                                       object: avPlayer.currentItem)
let skScene = SKScene(size: CGSize(width: 1000 * mediaAspectRatio, height: 1000))
if resolution.1?.b != 0{
    skScene.size = CGSize(width: 1000, height: 1000 )
    skScene.zRotation = 1.5708
} else if resolution.1?.a != 1.0 {
    skScene.zRotation = 1.5708 * 2
}

let skNode = SKVideoNode(avPlayer: avPlayer)
skNode.position = CGPoint(x: skScene.size.width / 2.0, y: skScene.size.height / 2.0)
skNode.size = skScene.size
skNode.yScale = -1.0
if resolution.1?.b != 0{
     skNode.zRotation = 1.5708
} else if resolution.1?.a != 1.0 {
     skNode.zRotation = 1.5708 * 2
}
skNode.play()
skScene.addChild(skNode)

SCNNodeにコンテンツを貼り付けます。

ViewController.swift
virtualBackgroundNode.geometry = SCNPlane(width: size, height: size)
let material = SCNMaterial()
material.diffuse.contents = skScene
virtualBackgroundNode.geometry?.materials = [material]
virtualBackgroundNode.scale = SCNVector3(1.7  * mediaAspectRatio, 1.7, 1)
sceneView.scene.rootNode.addChildNode(node)

バーチャル背景をカメラの前に置く

ViewController.swift
let cameraPosition = sceneView.pointOfView?.scale
let position = SCNVector3(cameraPosition!.x, cameraPosition!.y, cameraPosition!.z - 10)
virtualBackgroundNode.position = position

バーチャル背景の位置を固定する

端末を動かすと、バーチャル背景がずれるので、カメラを追いかけるようにさせます。

スクリーンショット 2020-07-21 0.24.39.pngスクリーンショット 2020-07-21 0.29.53.png

rendererメソッドでバーチャル背景にカメラ前ポジションを伝えます。

ViewController.swift
var frontOfCamera = SCNVector3()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
     let position = SCNVector3(x: 0, y: 0, z: -10.0) // ノードの位置は、左右:0m 上下:0m 奥に50cm
        if let camera = sceneView.pointOfView {
            virtualBackgroundNode.position = camera.convertPosition(position, to: nil)
            virtualBackgroundNode.eulerAngles = camera.eulerAngles
        }
    }

バーチャル背景の大きさを変えられるようにする

ユーザーがピンチ・ジェスチュアで背景の大きさをちょうど良いサイズに変えられるようにします。

ViewController.swift
var lastGestureScale:Float = 1
    
@objc func scenePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
     switch recognizer.state {
     case .began:
         let logation = recognizer.location(in: sceneView)
         let hitResults = sceneView.hitTest(logation, options: [SCNHitTestOption.ignoreHiddenNodes:true])
         if hitResults.count > 0 {
             guard let node = hitResults.first?.node else {return}
             materialNode = node
         }
         lastGestureScale = 1
     case .changed:
         let newGestureScale: Float = Float(recognizer.scale)
         
         let diff = newGestureScale - lastGestureScale
         
         let currentScale = virtualBackgroundNode.scale
            
         virtualBackgroundNode.scale = SCNVector3Make(
             currentScale.x * (1 + diff),
             currentScale.y * (1 + diff),
             currentScale.z * (1 + diff)
         )
         lastGestureScale = newGestureScale
     default :break
     }
}
ViewController.swift
let pinch = UIPinchGestureRecognizer(
            target: self,
            action: #selector(type(of: self).scenePinchGesture(_:))
        )
        
pinch.delegate = self
sceneView.addGestureRecognizer(pinch)

ピンチ・ジェスチュアでSCNNodeを操作する方法は、天才プログラマーK-Boyさんのコードをお借りしました。ありがとうございます。

こんな感じで機械学習やARに関する情報を発信しています。
https://twitter.com/JackdeS11

rockyshikoku@gmail.com

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?