iOS
SceneKit
ARKit

SceneKitのサンプルにアニメーション付きの3Dモデルを表示させる

はじめに

デフォルトのSceneKitのサンプルをビルドするとクルクル回る宇宙船が表示される。

default_sample.gif

これをこのような自分オリジナルのアニメーション付きの3Dモデルに置き換えたい。
guy.gif

ビルド環境

  • macOS 10.12.6
  • Blender 2.79
  • MakeHuman 1.1.1
  • Xcode 8.3.3

注意) BlenderとXcodeのインストールは本投稿では扱わない

手順

  1. MakeHumanを使って3Dモデリングデータを作成する
  2. Blenderのアドオンを使って3Dモデルデータにボーンを埋め込む
  3. 3Dモデルデータにrigをつける
  4. ボーンにオートIKを設定する
  5. アニメーションを設定する
  6. 3Dモデルデータをdae形式でエクスポートし、Xcodeで読み込む
  7. Xcodeで3Dモデルデータを表示する

MakeHumanを使って3Dモデリングデータを作成する

3Dモデリングなんてやったことない・・・。安心してください MakeHuman を使えば簡単に3Dモデリングデータを作ることができます。

  1. MakeHumanのサイトにアクセスし、DownloadのリンクよりMakeHumanをダウンロードする
  2. Mac版の場合はdmg形式で配布されているためダウンロードしたdmgファイルを開きMakeHuman.appを/Applicationsへコピーする
  3. LaunchPadまたはFinderのApplicationsよりMakeHumanを起動する
    注意)MakeHumanには署名が付いていないので起動に失敗した場合はシステム環境設定>セキュリティとプライバシーより実行を許可する必要あり
  4. MakeHumanを使い人間のモデリングを行う
    画面にタブがあるが、今回使うのはModelingとGeometriesとMaterialsのみでOK
    注意)Pose/AnimeのタブでMakeHuman時点でrigを設定できるが、BlenderでインポートするとオートIK設定をしても上手く動かないため、MakeHumanでモデルだけ作成してBlenderでrigを設定する方をお勧めする
    makehuman_launch.png

  5. 作成し終わったら上部のアイコンの左から3番目を押しデータをエクスポートする、このとき出力フォーマットはCollada(dae)を選択しておくこと
    makehuman_save.png

Blenderのアドオンを使って3Dモデルデータにボーンを埋め込む

  1. Blenderを起動する
  2. デフォルトのプロジェクトではキューブが存在するが、これを右側のアウトライナーのCubeを右クリック>削除を選択し削除する blender_init.png
  3. メニューのファイル>インポート>Collada(デフォルト)(.dae)を選択し先ほどエクスポートした3Dモデルデータを読み込む
    blender_import.png

  4. Shift+Aを押してプリセットの人体ボーンを追加する
    注意)事前にプラグインを有効にしている必要があるため、設定していない場合はBlenderでRigifyを使う方法を参照して設定を行う
    add_armature.png
    not_set_sleleton.png

  5. 初期状態だと3Dメッシュにボーンが埋もれてしまい操作しづらいため、右のプロパティペインでアーマチュアを選択しスケルトンにチェックを入れる
    set_skeleton.png

  6. オブジェクトモードの表示になっているのでTabを押して編集モードに切り替え、各ボーンの位置を調整しメッシュの中にボーンが埋まるようにする
    この時Blenderでボーンを設定する時のテクニックのように対称化を使うと左右対称にボーンを埋め込むことができます
    set_bone.png

3Dモデルデータにrigをつける

rigを付けるのはオートウェイトを使えば簡単です。

  1. 編集モードからTabを押してオブジェクトモードに戻す
  2. 体のメッシュを右クリックで選択する
  3. Shiftを押しながら他のメッシュパーツをクリックして全てのメッシュを選択する
    赤枠で囲っているのが全てのメッシュを選択した状態で逆三角のアイコンが赤くなっている状態であればOK
  4. そのままShiftを押したままボーンを右クリックして選択する
  5. その状態でCtrl+Pを押して「自動のウェイトで」を選択する
    auto_weight.png

オートウェイトが成功すると、ポーズモードで下のようにボーンを動かして3Dモデルに好きなポーズを付けることができる。
pose.png

ボーンにオートIKを設定する

この状態だとポーズを付けるのに一つ一つのボーンを動かさなければならず、例えば膝を曲げるにも腿からつま先までを動かさなければならず非常に面倒臭い。オートIKを付けると人形のようにあるボーンを動かすと他のボーンが連動して動いてくれるようになりかなり編集が楽になります。

  1. ポーズモードに切り替える
  2. 左のペインのオプションタグを選択する
  3. オートIKにチェックを付ける
    autoIK.png

アニメーションを設定する

ようやくアニメーションを設定することができます。

  1. ポーズモードに切り替える
  2. Cを押し範囲選択で全ボーンを選択する
  3. 選択した状態でIを押し、キーフレームの挿入 >「位置/回転」を選択する add_first_frame.png
  4. この状態で全ボーンの初期状態のキーフレームが挿入されたので、画面最下部のタイムラインで時間を30に進める timeline.png
  5. 手足のボーンを適当な位置に動かし、先ほどと同じく範囲選択で動かしたボーンを選択してキーフレームを挿入する add_second_frame.png
  6. 最初のフレームまでタイムラインを戻し全ボーンを選択し、ポーズツールのコピーボタンを押し、初期状態のポーズをコピーする copy_first_frame.png
  7. 60までタイムラインを進め、ポーズツールの貼り付けボタンを押し、初期状態のポーズをペーストする paste_frist_frame.png
  8. ポーズのペーストを駆使して30ごとポーズを決めて、キーフレームを挿入して行く
  9. 120までポーズを決めたらタイムラインの終了を120に設定する
    change_animation_time.png

ここまで設定が完了した状態でタイムラインの再生ボタン:arrow_forward:を押すと下記のようなアニメーションになる。
anime_in_blender.gif

3Dモデルデータをdae形式でエクスポートし、Xcodeで読み込む

  1. Blenderの左上のメニューより ファイル > エクスポート > Collada(デフォルト)(.dae) を選択する
  2. 適当な名前をつけて保存する(例ではguy.daeをという名前を付けました) export.png
  3. Xcodeを起動する
    xcode_start.png

  4. Xcodeのメニューより File > New > Project を選択する

  5. iOSアプリケーションのGameを選択する
    xcode_select_project.png

  6. Game Technology で SceneKitを選択する
    xcode_project_option.png

  7. Xcodeのプロジェクトが完成したら先ほどのdaeファイルとMakeHumanエクスポート時に作られたtextureディレクトリをart.scnassets配下にコピーする
    add_dae.png

  8. この状態だとテクスチャが何も設定されていない状態なので、guy.daeを選択し左のペインでguy-baseという名前がついたノードを選択し、右ペインのプロパティでMaterialsを選択し、Diffuseでtexture内の皮膚画像を選択する
    set_texture.png

  9. 同様に他のパーツのテクスチャを設定する

Xcodeで3Dモデルデータを表示する

あとは簡単です。GameViewController.swiftを下記のように修正してください。

    override func viewDidLoad() {
        super.viewDidLoad()

        // create a new scene
-        let scene = SCNScene(named: "art.scnassets/ship.scn")!
+        let scene = SCNScene(named: "art.scnassets/guy.dae")!

        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)

        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)

        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)

        // retrieve the ship node
-        let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
+       // let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!

        // animate the 3d object
-        ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
+       // ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))

        // retrieve the SCNView
        let scnView = self.view as! SCNView

        // set the scene to the view
        scnView.scene = scene

        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true

        // show statistics such as fps and timing information
        scnView.showsStatistics = true

        // configure the view
        scnView.backgroundColor = UIColor.black

        // add a tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)
    }

この状態で実行すると、最初に紹介したような動く人体モデルが表示されます。
お疲れ様でした。