第2章 ARKit 堤 修一/@shu223
「空中に絵や文字を描く」というのはARの分野でも典型的なアプリケーションのひとつではありますが、デモとしては非常にわかりやすくインパクトがあります。トラッキングの精度が優れていないと描いた線が意図どおりに配置されずバラバラになってしまうので、ARKitの精度(トラッキング品質)を示す格好の題材でもあります。
本節では「iOSデバイスを動かして空中に線を描く」アプリケーションの実装方法を示します。
サンプルコード: 08_ARDrawing
2.9.1 実装方針
デバイスを動かして空中に線を書くためには、シンプルに各時点でのデバイスの位置(ワールド座標)を繋いで線を構築するという方針が考えられます。この方針を具体的な実装に落とし込むと次のようになります。
■ARSessionDelegateのsession(_:didUpdate:)またはSCNSceneRendererDelegate(ARSCNViewDelegateが継承している)のrenderer(_:updateAtTime:)をデバイスの位置を取得するタイミングとする
■「各時点でのデバイスの位置」としてスクリーンの中心座標をワールド座標に変換したものを用いる
■各時点でのワールド座標を頂点としてSCNGeometrySource、SCNGeometryElementを用いて線としてのカスタムジオメトリを構築する
何をデバイス位置としてどう計算するか、またそれらを頂点として線をどう描画するかは他にも多くの方法が考えられますが注25)、本節ではこの方針に沿って実装していきます。
2.9.2 スクリーンの中心座標をワールド座標に変換する
前述の方針通り、デバイスの位置は「スクリーンの中心座標をワールド座標に変換したもの」を用います。その変換には、SCNSceneRendererプロトコル(ARSCNViewの親クラスSCNViewが準拠している)に定義されている、2次元のスクリーン座標をワールド座標に変換するためのunprojectPoint(_:)メソッドが利用できます。
func unprojectPoint(_ point: SCNVector3) -> SCNVector3
引数にはスクリーン座標を渡すのですが、その型は3次元ベクトルになっています。3次元空間における座標を決めるためには、スクリーンに対する奥行き方向も指定しないと候補が無限に存在してしまうからです。
このSCNVector3型のz値には0.0〜1.0を指定します。0.0を指定するとレンダラの視錐台における手前側のクリッピング平面上にある座標が得られ、1.0だと遠方側のクリッピング平面上の座標が得られることになります。
スクリーンの中心座標を取得して、ワールド座標に変換するまでの実装を次に示します。
// スクリーンの中心座標を取得 let screenBounds = UIScreen.main.bounds let centerPos = CGPoint(x: screenBounds.midX, y: screenBounds.midY) // 3次元ベクトルにする let centerVec3 = SCNVector3Make(Float(centerPos.x), Float(centerPos.y), 0.99) // unproject(ワールド座標に変換)する let worldPos = sceneView.unprojectPoint(centerVec3)