ジャックランタンをモチーフにしたキャラクターの 3D モデルを作った①
korechipostit.hatenablog.com
と、第3回に渡って Maya を勉強したのでさっそく実践してみたいと思います!
目指せ3Dモデラーエンジニア!
目的
- 3Dモデルを作成する練習
- 今後作るゲーム用素材を自前で用意したい
- バーチャルYouTuberに使うアバターとして
環境
成果物
2Dイラスト
iPad Pro+Apple pencil を使って3D モデルの元となるコンセプト絵を描きました。製作時間は3時間ほど。
陽気なイメージのキャラクターで、昔から大好きなジャックランタンをモチーフにしました。
Adobe Illustrator Drawは個人的にかなり使いやすかったです。
長押しで塗りつぶし機能が使いやすく、筆先の太さ調整もとても簡単なので、自分のような初めてお絵かきする人にも使いやすいアプリだなと感じました。
また、ズームイン・ズームアウトもしやすくてGoodです!しばらくお絵かきをする際にはこのアプリを使おうと思います。
ちなみにスケッチをするならAdobe Photoshop Sketchというアプリが無料であるのでいつか使ってみたいです。
3Dモデル
こちらは Maya を使って作りました。製作時間は5時間ほど。
参考にしたサイトはこちらです。
maya.indyzone.jp
これを参考にしてかぼちゃ部を作成しました。
マスク・口元は頂点を気合で編集して作成しました。ここはもっと効率的な作り方があると思うので割愛。
学んだこと
上記にある Maya の公式ページで学んだこと
特殊な複製
編集->特殊な複製(オプション)
でさまざまな複製ができる。例えば下のようなオブジェクトを複数個回転しながら複製することが可能となる。
↓
y 軸(上から見て)を中心に45度回転しながら7個オブジェクトを複製すると、ぐるっと一周させることができる。
近接する頂点同士の結合
上の方法でオブジェクトを複製すると、境界部分のエッジ・頂点が重なってしまうという問題が残る。
アトリビュートエディタ->境界の表示
をオンにし、境界幅を調整するとエッジが重なっている部分が太く表示される。
メッシュの編集->マージのオプション
でしきい値を小さい数値に設定すると
↓
うまく結合でき、境界線がなくなるはずです。
ベンド
折り曲げる機能。参考URL
デフォーム->ノンリニア->ベンド
を選択します。
すると折り曲げる線が表示されるので、その線を位置・回転させつつ、アトリビュートエディタにある曲率を変化させて対象のオブジェクトを折り曲げる。
この機能を使うことでキャラクターについている仮面や口元を曲げることができた。
↓
今後の作業予定
- ステッキ部の3Dモデルを作成
- Unityへのインポート
- Unityで撮影
- 目と口のアニメーション(余力があれば)
絵心皆無のエンジニアの俺がMayaを勉強して3Dモデルを作ってみる【第3回】
前回の記事の続きです。
korechipostit.hatenablog.com
Macでの操作
Macにはスクロールマウスが存在しません。
Autodesk Maya オンライン ヘルプ
ここを参考にしたところ、Command+左クリック=スクロールマウス、となるようです。
11. 基本マテリアルとUVマッピング
球を作成するとlambert1
というマテリアルが初めから設定されている。
マテリアル、シェーダー、テクスチャについて
- マテリアル
- どのようにモデル表面を描画するかを定義`
- 使用するテクスチャ、そのタイリング設定や色調などへの参照をふくむ
- どのようにモデル表面を描画するかを定義`
- シェーダー
- テクスチャ
- ビットマップ画像
- 反射率や粗さなどのマテリアル表面の様々な要素をテクスチャで置き換えることが可能
Lambert
光沢がないマテリアル。コンクリートやゴムなどマッドな質感に向いている
Blinn
ハイライトが設定できるマテリアル。金属の質感に向いている
Phong
シャープなハイライトが設定できるマテリアル。プラスチックや陶器に向いている
新しいマテリアルを設定
右クリック->新しいマテリアルの設定
もしくは右クリック->お気に入りのマテリアルの割り当て
UVマッピング
UV=テクスチャの座標
ボールに世界地図を貼る
に例えられる。3Dの球に、2Dの地図を貼り付けるような考え。
#### 平面マッピング プレーンを通ってメッシュ状にUVを投影する
円柱マッピング
メッシュの周囲にラップされた円柱シェイプに基づいてUVが生成
球面マッピング
メッシュの周囲にラップされた球シェイプに基づいてUVが生成
自動マッピング
周囲に配置された複数のプレーンから同時に投影し最適なUVの配置を探しポリゴンメッシュにUVを作成
手順
- プリミティブなボックスを作成
メニュー->UV->UVメニュー
- プリミティブなオブジェクトだと既にUV座標を持っている
- テクスチャ境界の表示を選択
- ボックスにUVがどのように反映されているか見える
- UVメニュー内で頂点を選択するとビューのどこの頂点に反映されるか確認できる
アトリビュートエディタ->カラーの横の[f:id:korechi:20180106094606p:plain]
を選択- ファイルを選択
- イメージの名前の横にあるフォルダのアイコンからテクスチャファイルを選択
- UV座標と一致している画像
12. 小物(剣)のUV展開
- 2ビュー表示
- ビューの中にある
パネル->パネル->UVエディタ
でUVエディタが開かれる - オブジェクトを全選択し、
メニュー->UV->自動
で6面から投影・展開される - シェル選択モードに変更し、ある程度ばらけさせる
- エッジを縫合してまとめる
- UVエディタの
イメージ->UVのスナップショット
を選択し、テクスチャを作成する- 今回は拡大・回転・移動を使ってU:1, V:1に合わせた
-
- UVのスナップショットでUとVの値を変更すると複数枚にテクスチャを分割できる
- 今回はjpegとしてテクスチャを保存
- 保存したテクスチャをPhotoshopなどを使って編集する
- 自分はPhotoShopを持っていないため
- 編集したテクスチャをMayaに取り込みテクスチャをオブジェクトに反映させる
マテリアル->カラー->ファイル
を選択し、テクスチャをロード- ペイントソフト等でテクスチャを反映しロードしたところなぜか反映されない。。なぜだ
- 6キーを押すor
シェーディング->ハードウェアテクスチャリング
をクリックしないとテクスチャが表示されないらしいです!!これで1週間潰した。。
- 6キーを押すor
エッジの縫合
エッジ選択モードに変更し、shift+右クリック
でエッジを移動して縫合を選択
- その後はG: 上の繰り返しである程度UVがまとまるまで作業を続ける
エッジを分離
エッジを選択し、エッジをカット
13. 頭部のUV展開
頭部のUV展開
- 頭部のバイザーを覗いた部分を選択し、UVエディタで確認。
メニュー->UV->円柱
を選択すると下のようにUVが展開される
- 切れ目が変な部分になっているためエッジの縫合を行い真ん中で分割されるように
UV->UVの縫合
とUV->UVのカット
を使用
- 上の方はあまり綺麗ではないが切れ目は頭の後ろのラインになった。
- UV選択モードで全UVを選択し、
修正->展開のオプション
を選択- Unfold3Dにチェックが入っていることを確認して適用
こんな感じ
- Unfold3Dにチェックが入っていることを確認して適用
バイザー部のUV展開
- まずは頭部を非表示にし、裏側のフェースを選択
UV->平面のオプション
を選択- 投影元をカメラに
裏側は出来たのでそれは端に避けておく
- 投影元をカメラに
- UVシェル選択モードにして裏側以外を選択
UV->平面のオプション
を選択- 投影元をZ軸に
しかし、一部重なっているためそこを修正しなければいけない
- 投影元をZ軸に
- UV選択モードにして全UVを選択し、
修正->展開
- それぞれ調整しつつまとめてUVのスナップショットを保存
オブジェクトの非表示
ctrl+H
- 再表示はshift+H
最後に
これでキャラクターのモデルを作成し、テクスチャを作成する方法が分かりました。
基礎の基礎ですが、これらの知識を使って応用していきたいと思います。
Maya Learning Channelは14回以降はアニメーションの話になるのでここで一区切りとしたいと思います!
絵心皆無のエンジニアの俺がMayaを勉強して3Dモデルを作ってみる【第2回】
前回の記事の続きです。
korechipostit.hatenablog.com
6. ペアレントとグループ化
- 階層構造を表示するためにはアウトライナーを表示する必要がある
- 左メニューのこれをクリックすると表示される
- アウトライナーはシーン内のオブジェクトを一覧で表示したもの
- Unity の Hirarchy っぽい
- オブジェクトの階層構造を変更するには真ん中マウスをドラッグ
- オブジェクトを一括選択し、pキー:最後に選択されたオブジェクトが親になるらしい(よくわからなかった)
- shift+P:親子解除
- グループ化
- 親子関係じゃなくてまとめる
- ctrl+G: グループ化
- スペース長押し→編集→グループ化解除
- 親子関係じゃなくてまとめる
7. 小物(剣)のモデリング Part 1
作成->ポリゴンプリミティブ->インタラクティブ作成
にチェックを入れるとインタラクティブにオブジェクトが作成できる(めちゃめちゃ便利)- インタラクティブ作成にチェックがないと原点にオブジェクトが作成される
- Mayaでは1=1cmのスケール
- 右下にあるディスプレイ->レイヤ->選択項目からレイヤを作成でレイヤが作成できる
- レイヤの中では、Visible, Template, Referenceの切り替えが可能
- ただレイヤを作るだけだと現在見えているものがレイヤに適用されないため注意
- Template: テンプレート化。ワークスペース内には表示されるが選択とスナップ先への指定ができなくなる
- 今回は大まかな外枠の作成に使う
- Reference: スナップ先にはできるが選択や修正はできない
- レイヤの中では、Visible, Template, Referenceの切り替えが可能
- 頂点の追加
- スペース->メッシュツール->マルチカットにして頂点選択モード
- Ctrl:エッジループ
- 真ん中マウス: エッジの中点
- shift+頂点のマージで頂点を一つに
- ベベル
- 新しいフェースに選択した各エッジを拡大し、ポリゴンメッシュのエッジを丸める
- shift+右クリック長押し
- 真ん中マウスを押すと割合が変化
頻繁に使った機能
- 頂点スナップ(頂点を選択してV押しながら移動)
- グリッドスナップ(頂点を選択してX押しながら移動)
- G: 前回の命令を実行
- spaceを一瞬押すと選択ビューが拡大表示される
8. 小物(剣)のモデリング Part 2
- shift+右クリック:フェースの押し出し
チャネルボックス->入力->深度の分割数
の数を変えると分割できる
エッジ選択モードにし、
メッシュツール->ターゲットの連結
- ただしマージ先がターゲットになっていたため設定からセンターに変更
- ベベルエッジ
- セグメント数は折り曲げる数(?)
完成物
剣が完成!これでHoloQuestの剣を自分で作れるようになった!笑
次はキャラクターのモデリング
9. キャラクタ(頭部)のモデリング
必要なもの
- 描きたいキャラクターを前から見た画像
- 描きたいキャラクターを横から見た画像
手順
作成->フリーイメージプレーン
アトリビュートエディタ->イメージの名前
のアイコンから画像を選択できるので、前から見た画像と横から見た画像を表示させる
- 2枚のイメージプレーンをレイヤに登録してReferenceにして選択ができないようにする
- 球を作成し頭の大きさにする
球->入力->polySphere1->高さの分割数
を変更してちょっと荒めにする- 画面を4分割し、shift+Fで球を全ウインドウにフィット
- 頂点モードに切り替えて正面、横から見た画像と重ねて調整
- エッジの押し出しをうまく使って首元を作成
- 顔の部分を少し凹ませる
便利な機能
- シンメトリ
- ポリゴンモデルの片側のディテールを反対側に反映することができる
- ツールボックスのシンメトリの軸をトポロジにし、軸にしたいエッジを選択するとその軸を対象としてエッジが選択可能になる
10. キャラクタ(バイザー部)のモデリング
手順
カーブ->サーフェス
から円を選択し大きさを調整- 2点選択しデタッチすることで2つのオブジェクトに分割し後ろのを消す
右クリック長押し->CV
でCV選択モードに切り替えてバイザーの上・下・真ん中3つを調整
- 3つの円を下から選択し、
メニュー->サーフェス->ロフト
でカーブからなめらかなサーフェスが作成される
メニュー->修正->変換->NURBSをポリゴンに
を選択してポリゴンモデルへ変換する- UとVをうまいこと調整
- バイザーに厚みをつけるためフェースの押し出しで厚みを-1にして内側に引き伸ばす
- フェースの押し出しを使いまくって隙間を作成
- 右半分を作成したら左半分を一度消し、
メニュー->メッシュ->ジオメトリのミラー
デタッチ
頂点を選択した場合、フェースによって共有されている頂点を複数の頂点に分離し結果としてオブジェクトが分割される。
space->カーブ->デタッチ
ノットの挿入
ノットが挿入される
space->カーブ->ノットの挿入
ポリゴンモデルとnurbsモデルの違い
構造が違う。現在(2017年)の主流は編集が楽なポリゴンモデル。
- nurbs
- カーブで形成されたモデル
- 表面は滑らか
- ポリゴン
- 点と線で形成されたモデル
- 表面は角ばっている
ヒストリの削除
今まで行った作業がチャネルボックスの入力に残っているので、編集->種類ごとに削除->ヒストリ
で削除できる
まとめ
ひとまず頭の部分が出来ました。完成物はこちら。
とても大変だ・・・
絵心皆無のエンジニアの俺がMayaを勉強して3Dモデルを作ってみる【第1回】
はじめに
最近3Dモデルを自分で作れたらなーと思う機会がひじょーーーに増えました。
HoloQuestで使うモンスターに3Dモデルが必要ですね。いつまでもスライムを使っていられません()
あと、最近流行っているバーチャルユーチューバーですね。すごくやってみたいんですが、3Dモデルがどうしても必要に。。
とは言え、AssetStoreからモデルを買ってもオリジナリティを出すのはなかなか難しいし、果たしてそれで良いのか感。
そこで!じゃあもう自分でやれるところまでやってみよう!と思い、今回3Dモデリングを1から勉強することにしました!
コードしか書いたことのない絵心皆無のエンジニアですが()、3Dモデルを1から勉強し始めてどこまで出来るようになるか、チャレンジしたいと思います!目指せバーチャル(イケメン)ユーチューバー!
モデリングソフト
- Maya 2018
- 初めは無料体験版を使う。期限が切れたらMayaLTを購入するかも
動作環境
勉強方法
よく分からないので、「Maya チュートリアル」と検索して出てきたMaya Learning Channelの動画を見て勉強します。
これ以降は進捗と、適宜自分用のメモを残すといった感じになります。
あくまで自分の勉強がメインなのでスクリーンショットとかはそんなに多く用意できないと思いますすみません。
1. Autodesk Maya のインタフェース概要
- Fキーを使って「モデリング」「リギング」「アニメーション」・・・を切り替えできる
ふむふむとチュートリアルを進めていたら動画にはあるのに自分の画面にはツールボックスがない!
調べてもよくわからずいろいろ触っていたら、画面右下のボタンを押したらプリファレンスが出てきて、インターフェース->UI要素->ツールボックスにチェックを入れたら無事ツールボックスが出てきた!
こういうチュートリアルと画面が違うってパターンは心臓に悪いな。。
そしたらまた再生コントロール画面も無い。。同じようにツールボックスにてタイムスライダ
にチェックを入れたら出てきた!ぐぬぬ
2. ビューポートカメラの操作/ビューポートシェーディング
- ビュー操作
- 回転:Alt+左ドラッグ
- Alt+Shiftで固定可
- 並行移動:Alt+真ん中マウス
- ズーム:Alt+右ドラッグ or スクロール
- Alt+Ctrl+左ドラッグで指定区域にズーム
- 画面フィット:オブジェクトを選択してF(例えば頂点のみ)
- 全体をフィットさせるにはA
- 回転:Alt+左ドラッグ
- ビューを変えるにはパネル->正投影などから選択する
- シェーディングのショートカット
- 4: ワイヤーフレーム
- 5: シェーディング
- 6: テクスチャ付シェーディング これがそれに対応しているっぽい
3. ホットボックスとマーキングメニュー
Spaceを長押しするとホットボックスが出るらしいがなぜか出てこない・・・
そしたらサブディスプレイにMayaが置いてあるとホットボックスが表示されず、メインディスプレイにMayaが置いてあるとホットボックスが表示されていた・・・何だこの仕様。バグかな?気を取り直して
- スペースキーを押すとホットボックスが表示され、さらに右ドラッグすると別のものも選択できる
- 作成項目からオブジェクトが作成できる
- マーキングメニュー
- オブジェクトを選択して右長押し
- ctl+右長押しするとさらにメニューが切り替わる
- shift+右長押しなどで押し出しとかもできる
- オブジェクトを選択して右長押し
4. 選択モードとマニピュレータ
- 選択
- 一括選択:左ドラッグ
- 選択解除:ctrl押しながら選択
- 追加選択:shift押して選択
- 選択切り替え:shift+ドラッグ
- 投げ縄選択
- ペイント選択
- ペイント範囲の調整の仕方は分からない・・・次必要になった時に改めて調べよう
- マニピュレータ
- 移動:W
- 回転:E
- スケール:R
- マニピュレータの大きさはテンキーの「+」「ー」で変更
- 軸固定移動
- 軸をドラッグor軸を選択して真ん中マウスをドラッグ
- shift+真ん中マウスドラッグでも軸固定並行移動
- ダブルクリックでループ選択
5. コンポーネント操作とスナップ機能
- ポリゴンメッシュは頂点、エッジ、フェースで構成
- 右長押しでモード切り替え
- 頂点情報
- 法線
- 法線の向きで裏表が決まる
- レンダリングする時にサーフェイスから光がどう反射するか計算
- モデルをつくる時はすべて表が見えるように注意すべし
- スナップ機能
- グリッドスナップ:最も近いグリッド(格子状)に沿って移動
- X押しながら
- ポイントスナップ:最も近いポリゴンの頂点に吸い付くように移動
- V押しながら
- カーブスナップ:カーブにそわせてスナップ
- C押しながら
- オブジェクトごとカーブにそわせることも可能
- グリッドスナップ:最も近いグリッド(格子状)に沿って移動
おわりに
Maya楽しいです!!
5回分、多分合計30分もない動画しか見ていませんが、分からないことだらけで調べながら進めてたら2時間以上かかりました。
Blenderはあまり自分にあっていなかったのか、3回チャレンジして全部1日で挫折しましたがMayaは続けられそう?かも。
ただ、分からないことがあって調べてもなかなか出てこない。僕の検索の仕方が悪いのもあると思いますが、参考ブログ的なのが少ないのかな。
モデラーさんはエンジニアみたいにブログにちょこちょこまとめることが多くはないのかも?
(MayaLTについて調べていたら、凹みTipsが出てきた時には笑いました)
分からないことがあったら自力で探すしかない。けどそれはそれで楽しい!
次回もちゃんと続きをやりたいと思います!
ではまた〜
iPhone をモーションコントローラとして使えるパッケージを作った【AR/VR向け】
はじめに
HoloQuestでも使用している、iPhoneの動きを別のアプリで取得してiPhoneと同じ位置に何かを表示するパッケージを作りました。
OculusやViveは専用モーションコントローラが存在しますが、HoloLensやGearVRには3次元位置が取得可能な専用モーションコントローラが存在しません。
そこで、こういったデバイスに対して汎用的に3次元位置や傾きを送信するアプリをiPhoneにインストールすることで、iPhoneを疑似モーションコントローラとして使うことが出来ます。
デモ
応用例
配布先
- ARMotionController
- https://github.com/korechika/ARMotionController
- AppStoreに"AR Controller"が配布済みなのでそちらをiPhoneにインストールするだけでもOK(日本のみ)
- ARMotionReceiver
使用するアプリ
スマートフォン上で動作し、スマートフォンの動きを送信するARMotionControllerと、それを受信するARMotionReceiverを使用します。
ARMotionControllerは多くのアプリに簡単に組み込めるため、お好きなデバイス向けにビルドして使用して下さい。
前提
スマートフォンとスマートフォンの位置を送信する相手(PC等)は同じネットワーク環境に繋がっている必要があります!
同じwifiを使うなり、テザリングなどを使用して下さい!
使い方
現在ARMotionControllerはまだiPhoneにしか対応していないため、以降はiPhoneを使って説明します。
簡単のため、iPhoneの位置送信相手もPCとします。
手順
- iPhoneとPCを同じネットワークに接続
- iPhoneにARMotionControllerをインストールし、アプリ起動(Unityを使っても、AppStoreからインストールしてもどちらでも可)
- PCのIPアドレスを調べてアプリ内入力欄に記入して待機(まだStartボタンは押さない)
- ARMotionReceiver.unitypackageをここからDLし、自分のUnityアプリケーションにインポート
Assets->Prefabs->HandTracker.prefab
とAssets->Prefabs->RightHandSample.prefab
をHierarchyに配置- Run
- iPhone側のアプリでStartボタンを押す
- 通信開始
今後
ARMotionControllerをARCore向けにも対応
ARKit を Unity エディタ上で動作確認出来る、ARKitRemoteについて調査した
ARKitRemoteとは
Unity エディタ上で ARKit アプリの動作確認を行うための、iOS 端末上で動作するアプリです。
背景
本来、Unityで開発しているアプリを iOS 端末上で動作確認するためには、いちいち Unity のアプリを Xcode 用にビルドし、それを Xcode 上でビルドして iOS 端末にインストールする必要があります。
それはめんどくさいです。Unity エディタ上だけで動作確認が出来れば非常に便利ですよね。
しかし、ARKit の特性上 iOS 端末からのカメラ映像やセンサーの値が必要です。
それらを iOS 端末から Unity エディタに送信して、Unity 側でよしなに受け取った値をもとに動作をエミュレートさせよう、というのが ARKitRemote シーンの役割です。
動作するアプリ
なので、まずは大きく2つのアプリが動作することになります。
- iOS 上で動作する、カメラ映像などを Unity エディタに送信するアプリ
- ARKitRemoteが動作
UnityARKitPlugin -> ARKitRemote -> ARKitRemote.Unity
のシーンそのもの- 以降リモートアプリと呼ぶ
- Unity エディタ上で動作するアプリ
- 本来作成したいアプリ
- 以降Myアプリと呼ぶ
動作環境
- Unity 2017.2.0f3
- iPhone6s plus (iOS11.2)
- Xcode 9.2
使い方
まずUnityRemoteKitの使い方は以下のサイトが参考になるので、読んで使ってみてください。
lilea.net
本記事では、ARKitRemote(と、Myアプリに何が必要か)について調査したいと思います。
ARKitRemote シーン
まず中のシーンを開いてみますと以下のようになっており、Main CameraとDirectional LightしかHierarchyにないことが分かると思います。
そして、この Main Camera にアタッチされている、ConnectToEditor.cs
とUnityRemoteVideo.cs
が ARKitRemote シーンのみに存在していることが他サンプルシーンと比較すると分かります。
(UnityARVideo.cs は他の ARKit サンプルシーンでも使われているスクリプトです)
そのため、上記の2スクリプトを見ていきます。
ConnectToEditor.cs
大まかに言うとこのクラスは、リモートデバイスでの変化(フレームの更新やAnchorの更新)のタイミングのたびに情報を Myアプリ(Editor上で本来動作してる)に送信するクラスです。
PlayerConnection playerConnection; void Start() { Debug.Log("STARTING ConnectToEditor"); editorID = -1; playerConnection = PlayerConnection.instance; playerConnection.RegisterConnection(EditorConnected); playerConnection.RegisterDisconnection(EditorDisconnected); playerConnection.Register(ConnectionMessageIds.fromEditorARKitSessionMsgId, HandleEditorMessage); m_session = null; }
Start() 時に PlayerConnection インスタンスを生成しています。
これはエディタ、プレイヤー間の接続を扱うクラスです。 この接続はプロファイラとプレイヤーを接続することで確立されます。
インスタンス生成後、接続開始時・終了時のコールバックを設定し、大事なのはその後の処理。
playerConnection.Register(ConnectionMessageIds.fromEditorARKitSessionMsgId, HandleEditorMessage);
の第1引数にはConnectionMessageIds.fromEditorARKitSessionMsgId
というのが含まれており、これはARKitRemote.unityと同じ階層にあるConnectionMessageIds.cs
にIDが設定されているのが確認できます。
このIDは、当然Myアプリ側から同じ値をリモートアプリに接続要求する際に送信されます。(詳しくは、ARKitRemoteConnection.cs参照)
第2引数には、コールバック関数が指定されるため、メッセージが届くたびに HandleEditorMessage () が呼ばれます。
void HandleEditorMessage(MessageEventArgs mea) { serializableFromEditorMessage sfem = mea.data.Deserialize<serializableFromEditorMessage>(); if (sfem != null && sfem.subMessageId == SubMessageIds.editorInitARKit) { InitializeARKit ( sfem.arkitConfigMsg ); } }
HandleEditorMessage 内では、メッセージをデシリアライズし、初期化関数が呼ばれます。
UnityARSessionNativeInterface m_session; void InitializeARKit(serializableARKitInit sai) { #if !UNITY_EDITOR //get the config and runoption from editor and use them to initialize arkit on device Application.targetFrameRate = 60; m_session = UnityARSessionNativeInterface.GetARSessionNativeInterface(); ARKitWorldTrackingSessionConfiguration config = sai.config; UnityARSessionRunOption runOptions = sai.runOption; m_session.RunWithConfigAndOptions(config, runOptions); UnityARSessionNativeInterface.ARFrameUpdatedEvent += ARFrameUpdated; UnityARSessionNativeInterface.ARAnchorAddedEvent += ARAnchorAdded; UnityARSessionNativeInterface.ARAnchorUpdatedEvent += ARAnchorUpdated; UnityARSessionNativeInterface.ARAnchorRemovedEvent += ARAnchorRemoved; #endif }
前半ではARKitに関する初期化を行い、後半ではフレームの更新や平面の検知・更新・削除の際のイベントに関数を追加しています。UnityARSessionNativeInterfaceクラスの中身の一部は以下のようになっています。
// UnityARSessionNativeInterface.cs public class UnityARSessionNativeInterface { // public delegate void ARFrameUpdate(UnityARMatrix4x4 cameraPos, UnityARMatrix4x4 projection); // public static event ARFrameUpdate ARFrameUpdatedEvent; // Plane Anchors public delegate void ARFrameUpdate(UnityARCamera camera); public static event ARFrameUpdate ARFrameUpdatedEvent;
ARKitでは水平面を自動で認識しそこにARAnchorを設置してくれるようになっています。
Anchors 以外のイベント(例えばARFaceAnchorAdded)もとれるみたいですが、今回は使いません。(FaceID関係かな?)
あとは、イベントが発生したタイミングで MyアプリにplayerConnection.Send()
を使って送信しています。
public void ARFrameUpdated(UnityARCamera camera) { serializableUnityARCamera serARCamera = camera; SendToEditor(ConnectionMessageIds.updateCameraFrameMsgId, serARCamera); } public void ARAnchorAdded(ARPlaneAnchor planeAnchor) { serializableUnityARPlaneAnchor serPlaneAnchor = planeAnchor; SendToEditor (ConnectionMessageIds.addPlaneAnchorMsgeId, serPlaneAnchor); } public void SendToEditor(System.Guid msgId, object serializableObject) { byte[] arrayToSend = serializableObject.SerializeToByteArray (); SendToEditor (msgId, arrayToSend); } public void SendToEditor(System.Guid msgId, byte[] data) { if (playerConnection.isConnected) { playerConnection.Send(msgId, data); } }
送っている情報は、ARFrameUpdated()では serializableUnityARCamera のインスタンス、ARAnchorAdded()(と書いていないがARAnchorUpdated()、ARAnchorRemoved())ではserializableUnityARPlaneAnchor のインスタンスが送られる。何が送られているのかちょっと覗いてみると
// ARKitRemote -> SerializableObjects.cs [Serializable] public class serializableUnityARCamera { public serializableUnityARMatrix4x4 worldTransform; public serializableUnityARMatrix4x4 projectionMatrix; public ARTrackingState trackingState; public ARTrackingStateReason trackingReason; public UnityVideoParams videoParams; public serializableUnityARLightData lightData; public serializablePointCloud pointCloud; public serializableUnityARMatrix4x4 displayTransform;
[Serializable] public class serializableUnityARPlaneAnchor { public serializableUnityARMatrix4x4 worldTransform; public SerializableVector4 center; public SerializableVector4 extent; public ARPlaneAnchorAlignment planeAlignment; public byte[] identifierStr;
serializableUnityARMatrix4x4というのはMatrix4x4クラスのようなものです。
全部の変数の型を調べてはいませんが、名前からなんとなく送られているデータを察することはできます。
pointCloud 変数には特徴点が含まれているのでしょうね
UnityRemoteVideo.cs
public ConnectToEditor connectToEditor; private UnityARSessionNativeInterface m_Session; public void OnPreRender() { ARTextureHandles handles = m_Session.GetARVideoTextureHandles(); if (handles.textureY == System.IntPtr.Zero || handles.textureCbCr == System.IntPtr.Zero) { return; } if (!bTexturesInitialized) return; currentFrameIndex = (currentFrameIndex + 1) % 2; Resolution currentResolution = Screen.currentResolution; m_Session.SetCapturePixelData (true, PinByteArray(ref m_pinnedYArray,YByteArrayForFrame(currentFrameIndex)), PinByteArray(ref m_pinnedUVArray,UVByteArrayForFrame(currentFrameIndex))); connectToEditor.SendToEditor (ConnectionMessageIds.screenCaptureYMsgId, YByteArrayForFrame(1-currentFrameIndex)); connectToEditor.SendToEditor (ConnectionMessageIds.screenCaptureUVMsgId, UVByteArrayForFrame(1-currentFrameIndex)); }
UnityRemoteVideo.cs
クラス内では、OnPreRender() が呼ばれており、これはカメラがシーンのレンダリングを開始する前に呼び出されます。関数の実行順はここを参考。
最終的に、connectToEditor.SendToEditor() でbyte[] を送信しています。
詳しくないですが、ここで送信されている Y やら UV は、色空間を表すものらしいので、おそらくこれがビデオ映像の一部ではないかと思われます。
Unity エディタ上で動作するアプリ
ふぅ長くなりましたね。次は、Myアプリ上での処理について見ていきましょう。
今回使用するのはUnityARKitPlugin -> ARKitRemote -> EditorTestScene.unity
です。
まずは、このシーン内で他の ARKit サンプルシーンと何が違うか見てみましょう。
ARKitRemoteConnection というプレハブと、HitCubeにEditorHitTest.cs
がアタッチされているのが分かると思います。それぞれ見ていきましょう。
ARKitRemoteConnection.cs
ARKitRemoteConnection プレハブは、ARKitRemoteConnection.cs がアタッチされているだけのオブジェクトです。
まずは、Start() の中の一部を見てみましょう。
// Start(): editorConnection = EditorConnection.instance; editorConnection.Initialize (); editorConnection.RegisterConnection (PlayerConnected); editorConnection.RegisterDisconnection (PlayerDisconnected); editorConnection.Register (ConnectionMessageIds.updateCameraFrameMsgId, UpdateCameraFrame); editorConnection.Register (ConnectionMessageIds.addPlaneAnchorMsgeId, AddPlaneAnchor); editorConnection.Register (ConnectionMessageIds.updatePlaneAnchorMsgeId, UpdatePlaneAnchor); editorConnection.Register (ConnectionMessageIds.removePlaneAnchorMsgeId, RemovePlaneAnchor); editorConnection.Register (ConnectionMessageIds.screenCaptureYMsgId, ReceiveRemoteScreenYTex); editorConnection.Register (ConnectionMessageIds.screenCaptureUVMsgId, ReceiveRemoteScreenUVTex);
EditorConnection のインスタンスを取得し、コールバック関数を複数設定しています。
EditorConnection はEditor側からプレイヤー側に接続要求をするクラスです。
AddPlaneAnchor() を見てみましょう。
void AddPlaneAnchor(MessageEventArgs mea) { serializableUnityARPlaneAnchor serPlaneAnchor = mea.data.Deserialize<serializableUnityARPlaneAnchor> (); ARPlaneAnchor arPlaneAnchor = serPlaneAnchor; UnityARSessionNativeInterface.RunAddAnchorCallbacks (arPlaneAnchor); }
メッセージをデシリアライズし、それをUnityARSessionNativeInterfaceのコールバック関数に登録しています。
こうすることで、カメラフレームの更新があるたびにそれがそのままUnityARSessionNativeInterfaceに送られます。
void ReceiveRemoteScreenYTex(MessageEventArgs mea) { if (!bTexturesInitialized) return; remoteScreenYTex.LoadRawTextureData(mea.data); remoteScreenYTex.Apply (); UnityARVideo arVideo = Camera.main.GetComponent<UnityARVideo>(); if (arVideo) { arVideo.SetYTexure(remoteScreenYTex); } }
こっちはテクスチャの方で、最後のarVideo.SetYTexure(remoteScreenYTex);
がいかにもリモートアプリから送られてきたテクスチャを Editor 上に表示させているっぽいですね。(詳しくは分かりませんがおそらくそう)
と、まぁ大まかに、
- Editor側からプレイヤー側に接続要求
- リモートアプリから受信した情報をUnityARSessionNativeInterfaceに送る
でいいのかな?(ちょっと自信ない)
EditorHitTest.cs
こっちは非常に単純です。
Editor上で疑似タップ操作を行えるように、Input.GetMouseButtonDown を Update 関数の中で呼んでいるだけです。
#if UNITY_EDITOR //we will only use this script on the editor side, though there is nothing that would prevent it from working on device void Update () { if (Input.GetMouseButtonDown (0)) { Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hit; //we'll try to hit one of the plane collider gameobjects that were generated by the plugin //effectively similar to calling HitTest with ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent if (Physics.Raycast (ray, out hit, maxRayDistance, collisionLayerMask)) { //we're going to get the position from the contact point m_HitTransform.position = hit.point; Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); //and the rotation from the transform of the plane collider m_HitTransform.rotation = hit.transform.rotation;
つまり、この関数がないと Editor 上でタップができなくなるので、ただ眺めるだけのアプリになります。笑