[iOS11][ARKit] オブジェクトの位置と回転「常にこっちを向いている怪獣」

2017.10.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

ARLKitでは、空間に自由にオブジェクトを配置することができます。そして、このオブジェクトを配置する位置は、3D空間上の座標(ワールド座標)で指定します。また、オブジェクト自体の向きや傾きも指定することが可能です。

今回は、この位置や向き・傾きの指定についてまとめて見ました。

2 固定位置への配置

以下のコードは、画面をタップしたタイミングで、色の違ったキューブを生成し、シーンの中に配置しているコードです。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
      let node = SCNNode() // ノードを生成
      node.geometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0) // 一片が10cmのキューブ
      let material = SCNMaterial() // マテリアル(表面)を生成する
      material.diffuse.contents = randomColor() // 表面の色は、ランダムで指定する
      node.geometry?.materials = [material] // 表面の情報をノードに適用

      node.position = SCNVector3(0, 0, -0.5) // ノードの位置は、原点から左右:0m 上下:0m 奥に50cmとする
      sceneView.scene.rootNode.addChildNode(node) // 生成したノードをシーンに追加する
}

キューブの位置 position は、カメラを原点として、左右0、上下0、そして奥に50cmとなっています。( SCNVector3(0, 0, -0.5)

そして、これを実行しているようすです。

起動時は、カメラが原点にいるので、指定した位置(カメラの中央)にキューブが配置されていますが、カメラを移動しても、キューブは同じ位置に生成されるため、カメラの中央では無くなります。

これは、ワールド座標が、起動時(初期化時)に決定され、その後も変化しないことが理由です。

指定した位置(SCNVector3(0, 0, -0.5))は、常に同じ場所を指しているわけです。

3 カメラの中央に配置

起動時(原点)から移動しても、常にカメラ画面の中央にキューブを配置するには、現在のカメラの位置を基準として、キューブ位置を計算する必要があります。

現在のカメラの位置は、sceneView.pointOfViewで取得可能なので、それを基準にconvertPosition(_:to:)メソッドで位置を計算します。

func convertPosition(_ position: SCNVector3, to node: SCNNode?) -> SCNVector3

常にカメラの中央にキューブを配置するコードは、以下のとおりです。

let position = SCNVector3(x: 0, y: 0, z: -0.5) // 偏差のベクトルを生成する
if let camera = sceneView.pointOfView { // カメラを取得
      node.position = camera.convertPosition(position, to: nil) // カメラ位置からの偏差で求めた位置をノードの位置とする
}

そして、こちらを実行するとこんな感じです。

4 カメラの向きに合わせる

常にカメラの中央にキューブを配置することができるようになりましたが、よく見てみるとキューブは、カメラに正対していません。 これも、先ほどの理由と同じで、起動時のZ軸を元にキューブが生成されるため、すべてのキューブが同じ方向に向くのです。

005

ノードのオイラー角は、eulerAnglesで指定できるので。生成したキューブを、現在のカメラと同じにしてみます。

let position = SCNVector3(x: 0, y: 0, z: -0.5)
if let camera = sceneView.pointOfView {
      node.position = camera.convertPosition(position, to: nil)// カメラ位置からの偏差で求めた位置
      node.eulerAngles = camera.eulerAngles  // カメラのオイラー角と同じにする
}

006

これを実行すると、常にカメラに正対して、キューブが生成されていることを確認できます。

5 回転させる

上記では、カメラのeulerAnglesを元に、オブジェクトの向きを変えましたが、数値などで直接オブジェクトの回転を指定する場合は、rotationの方が分かりやすいかも知れません。

004

// X軸を中心に45度回転させる
node.rotation = SCNVector4(1, 0, 0, 0.25 * Double.pi)

001

// Y軸を中心に45度回転させる
node.rotation = SCNVector4(0, 1, 0, 0.25 * Double.pi)

002

// Z軸を中心に45度回転させる
node.rotation = SCNVector4(0, 0, 1, 0.25 * Double.pi)

003

rotationSCNVector4で指定されます。 SCNVector4は x, y, z, w それぞれの角度(ラジアン)で指定しますが、合成ができないため、複数軸に対する回転を指定するためには、やはりeulerAnglesが必要です。

6 常にこっちを向いている怪獣

最後に、eulerAnglesを使用して、常にカメラの動きに合わせて向きを変える怪獣を作って見ました。

違いが分かりやすいように、2匹の怪獣を生成し、左の怪獣だけを追従させています。

7 最後に

今回は、ノードの位置や回転について纏めてみました。ワールド座標が、シーンの初期化時に決定されるため、カメラの動きに応じた動作を考えると、常に変換が必要になります。ちょっとややこしいですが、慣れるしかないですね。

コードは、下記に置きました。参考になれば幸いです。
github [GitHub] https://github.com/furuya02/ARKitPointOfViewpointSample

怪獣のモデルは、下記のものを利用させて頂きました。
https://free3d.com/3d-model/charizard-85299.html

8 参考リンク


Introducing ARKit
Apple Developer > Documentation > ARKit
[iOS 11] はじめてのARKit #WWDC2017
[iOS 11][ARKit] 距離の計測について #WWDC2017
[iOS 11][ARKit] 平面の検出について #WWDC2017
[iOS 11][ARKit] 物理衝突を実装して、キューブを落として見る #WWDC2017
[iOS 11][ARKit] 空間に3Dテキストを表示してみる #WWDC2017
[iOS 11] ARKitでソファー設置からあの子の身長測定まで色々やってみた
[iOS 11][ARKit] 近所の駐車場で、大型ディスプレイでビデオを見る!