ARKitを使って線を書けるアプリで有名なのがJust a line、World Brushなど。その他にもFacebookのカメラでも空間に落書きできたり、落書きアプリというのはいくつかあります。
また僕の所属するGraffityでも空間に落書きするアプリを作ってきました。
今記事では、どういった原理で空間に落書きしているのか解説します。
一番カンタンな線を書く方法
「カメラから一定の距離に球を置き、その連続で線を作る」という方法がもっとも簡単です。
連続で点を打つ | 点を打つスパンを短く |
---|---|
// タップした状態で指を動かすと呼ばれるメソッド
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)
}
フレームの更新を利用する方法
先程の方法だと、指の動きをトラックしているだけなので、デバイスを動かしたときに動いたとみなされず、お絵かきの幅が広がりません。
以下のように、踊りながらお絵描きができないわけです。
芸術っぽさを感じる pic.twitter.com/Qusha5OtpH
— K-BOY@ARエンジニア (@kboy_silvergym) 2018年12月9日
そこで、フレームの更新を利用する方法があります。タッチ中はフラグを立てておいて(必要に応じてタッチのポジションを先の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というアプリを見てみましょう。
3DBrushは球の連続で線を書くのではなく、1本の線単位の曲線のジオメトリを持つSCNNodeを作って置いているように見えます。よりメモリ効率の良い、より綺麗な線を描画するためにはこのアプローチが良さそうです。
では、どのようにこのアプローチをしていくのでしょうか?
こちらは、また明日の記事で上級編として書きたいと思います。
では!