7
6

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 5 years have passed since last update.

ARKitAdvent Calendar 2018

Day 9

ARKitを使って空間に線を描く方法(初級編)

Last updated at Posted at 2018-12-09

ARKitを使って線を書けるアプリで有名なのがJust a lineWorld Brushなど。その他にもFacebookのカメラでも空間に落書きできたり、落書きアプリというのはいくつかあります。

また僕の所属するGraffityでも空間に落書きするアプリを作ってきました。

今記事では、どういった原理で空間に落書きしているのか解説します。

一番カンタンな線を書く方法

「カメラから一定の距離に球を置き、その連続で線を作る」という方法がもっとも簡単です。

連続で点を打つ 点を打つスパンを短く
GIFイメージ-F6AAB9EDF2C6-1.gif GIFイメージ-628FB6E0CF25-1.gif
// タップした状態で指を動かすと呼ばれるメソッド
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let point = touches.first?.location(in: sceneView) else {
        return
    }
    let point3D = sceneView.unprojectPoint(SCNVector3(point.x, point.y, 0.997))
    
    // 球のNodeを作る
    let ball = SCNSphere(radius: 0.005)
    ball.firstMaterial?.diffuse.contents = UIColor.red
    let node = SCNNode(geometry: ball)
    node.position = point3D
    sceneView.scene.rootNode.addChildNode(node)
}

フレームの更新を利用する方法

fps

先程の方法だと、指の動きをトラックしているだけなので、デバイスを動かしたときに動いたとみなされず、お絵かきの幅が広がりません。

以下のように、踊りながらお絵描きができないわけです。

そこで、フレームの更新を利用する方法があります。タッチ中はフラグを立てておいて(必要に応じてタッチのポジションを先のtouchesMovedメソッドで取得し)、フレームが更新するたびに呼ばれるARSCNViewDelegateのrenderer(:updateTime)の中で球を置いていくパターンです。

ちなみにARKitでは、デフォルトでは60fpsなので、1秒間に60回フレームが更新されてカメラが動画として見えます。つまり、この方法では1秒間に60回球を置いておくことになります。

private var pointTouching: CGPoint = .zero

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    isTouching = true

    guard let location = touches.first?.location(in: nil) else {
        return
    }
    pointTouching = location
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let location = touches.first?.location(in: nil) else {
        return
    }
    pointTouching = location
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    isTouching = false
}

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    guard isTouching else {return}
    
    // 球のNodeを作って置く
    let point3D = sceneView.unprojectPoint(SCNVector3(pointTouching.x, pointTouching.y, 0.997))

    // 球のNodeを作る
    let ball = SCNSphere(radius: 0.005)
    ball.firstMaterial?.diffuse.contents = UIColor.red
    let node = SCNNode(geometry: ball)
    node.position = point3D
    sceneView.scene.rootNode.addChildNode(node)
}

球を連続で置くことに対する課題

ここまで述べたように、「球を連続に置く」ことで線が出来上がります。しかしSCNNodeを短い時間に連続で置きまくるととても重くなります。そして、そんなにきれいな線じゃないですよね。

ここで3DBrushというアプリを見てみましょう。

GIFイメージ-D660F2DDC91F-1.gif

3DBrushは球の連続で線を書くのではなく、1本の線単位の曲線のジオメトリを持つSCNNodeを作って置いているように見えます。よりメモリ効率の良い、より綺麗な線を描画するためにはこのアプローチが良さそうです。

では、どのようにこのアプローチをしていくのでしょうか?

こちらは、また明日の記事で上級編として書きたいと思います。

では!

7
6
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
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?