Unityでプロジェクションマッピングをした話

先日の徳山高専祭で展示した「Re:ink」にて、どのような工程でプロジェクションマッピングを行ったかの解説記事になります。
徳山高専祭のこと、メイン企画のことと、両方とも素晴らしいので語ればきりがないのですが、できるだけプロジェクションマッピングに要点を絞って解説します。
来年以降の後輩、あるいは他高専の皆さまの参考になればと思います。

メイン企画、Re:inkの説明は動画の説明欄から!


メンバー構成

2015年度のメイン部署の構成はこのとおり
機械電気工学科4人(4年4人)
情報電子工学科7人(4年3人, 3年2人, 2年2人)
土木建築工学科6人(4年4人, 3年1人, 2年1人)
うち、3人が去年からの引き続きメンバーです。
(ちなみに高専3年生=高校3年生, 高専4年生=大学1年生)

情報電子工学科の内訳を詳しくみると
ゲーム班(3年2人) - Unityによるゲーム制作とKinect部分の制作が担当。こちらの2人とも大掛かりなプログラムを書くのはこれが初めてです。
映像班(4年1人2年1人) - Unityによる映像制作とプロジェクションマッピングが担当。4年生は僕です。2年の後輩が優秀すぎました。
サーバ班(4年2人) - 全体統括のサーバ開発、Webコントローラ開発、VLAN構築が担当。両方とも高専プロコン経験者のベテランです。

システム構成

スライド1
システム構成を簡略化したものです。一つの大きな部屋を木材やカーテン等で仕切っています。来場者は待機室で順番を待ち、順番がきたら2~4人で組を作ってゲームブースに進みます。ゲームをクリアしたら映像ブースに進み、映像を鑑賞した後に出口へ出る流れです。本当ならここに「現場中継のためのVLAN」と「馬車とWindows間通信のZigBee」が加わりますが簡略化のために省略しています(自分が関わったわけではなく詳しくないというのもある)。

ゲームブース

20151101_2843
ゲームはKinectを用いたジェスチャーゲームで、2~4人でプレイしゴールまでに獲得した「花」の数を競います。Kinect v2を使用するためWindows8にする必要があり、これの環境を用意するのに何気に苦労しました。スマートフォンから専用Webサイトを開きプレイ人数を入力し開始ボタンを押すと、その情報がサーバに伝わり、サーバからTCPWin8にコマンドが送られます。コマンドを受け取ったWin8側はゲームを開始します。ゲームが終了すると、獲得した「花」の個数をサーバに送ります。

映像ブース

映像ブースは4台のプロジェクターを使用するため、Macを4台用意しています。Unityからの出力映像を模型に合うようにマッピングします。マッピングさせるためにMeshWarpServerを使用しており、このソフトにSyphon経由で映像を渡すため、Macでないといけません。当初の予定では、MacMini×1, MacbookAir×3の構成で想定していましたが、本番直前にMacbookProを借りることができたため、MacMini×1, MacbookPro×3の構成に切り替えました。これは本当に有難かったです。
ゲームブースで獲得した「花」の全来場者分の総本数で映像は変化します。サーバから定期的に「花」の数が送られてきて、その数をクライアント内で更新しています。 スマートフォンからWebブラウザを開き、専用サイトにアクセスし、開始ボタンを押すと、サーバを経由して一斉にMac側に送信コマンドが送られます。送信コマンドを受け取ったMac側が同時に再生を開始することで、映像を同期させています。

問題発生

当初は映像の同期をOSCで実装していました。が、開始のタイミングだけ合わせればよいということ、サーバの開発者と協議してUDPに確実性が求められないことからTCPに切り替えました。これが展示1週間前のことです。TCP通信は去年の大蛇の乱でも使用しており、クライアント(Unity, C#)、サーバ(Ruby)ともに去年のライブラリやノウハウがそろっている状態でした。この時点で3台の同時再生を確認しており、ほぼラグのない状態でした。 ところが、展示開始3時間前になって問題が発生しました。TCPで通信した際に映像にラグが出たり、再生が開始されないクライアントが出現したのです。結局、展示開始時間になっても不安定なのは改善されず、Macごとに人員を配置し「3, 2, 1, スタート」の掛け声で同時にキーボードを押すことで映像を同期させました。人力最強。

計画(2月~6月)

実験(4月~8月)

初めてやることなので、上手くいくかどうか4回にわたって実験を行いました。小さめの模型を実際に設計から製作まで行い、それに映像を投影してみるといった内容です。1回目はOpenFrameworksを用いた投影。うまくいきますが複雑なモデルだと厳しそうと判定しました。2回目はMeshWarpServerを使用してみますが使い方に苦戦し上手くいかず、3回目でMWServerを使ったマッピングに成功します。4回目でUnityの映像を渡してインタラクティブに変化を加えることができ、実験を終わりとしました。 20150323_6251

模型設計(7月~8月)

まずは、投影する模型の設計を行います。この作業は土木建築工学科の学生が行いました。 真ん中の模型配置変更 工場模型 ①平面図

模型製作(8月~9月)

メイン部署の土木建築工学科の学生中心に、部署外の他の高専祭実行委員にも手伝ってもらいながら模型を製作します。大きいもので高さは2m弱。素材はスチレンボードを使用しています。 20150323_8583

3Dモデリング(8月)

設計の段階で建築科の学生が制作した設計図を元に、Blenderで3Dモデルに起こします。 YwmvCVv6

テクスチャ(9~10月)

BlenderでUV展開し、UVの枠組みを画像にエクスポート。それをGIMPで読み込みテクスチャを描きます。テクスチャは「ベースのテクスチャ, 汚れテクスチャ1, 汚れテクスチャ2, 汚れテクスチャ3」の3枚で構成されるようにレイヤー分けしています。
daikei daikei_kitanai

Unityによる映像制作(8~10月)

製作した3Dモデルとテクスチャを読み込み、Unityで映像を作っていきます。
今回は、音楽に同期して処理が走ります。MusicEngineを使用しました。音楽のタイミングに合わせたり、現在の状態で遷移したりするのにとても便利なクラスです。プログラムもこのクラスの使用法に合わせて設計しています。

Unityの画面はこんな感じ スライド2
左側にあるのはコントロールUI。再生の開始の他、曲の特定の位置へのシークができます。この画面をそのままMWServerに持っていくのですが、カメラからの映像のみを持っていくため、uGUIのコンポーネントは表示されません。そのため、画面の上に好きなだけデバッグ用のUIを仕込むことができます。便利。

Unity5で背景を暗くするには、DirectionalLightを2つ設置します。最初から設置されていたDirectionalLightは太陽となるため、Rotationを回すと背景も変化しますが、今回の場合は都合が悪いです。そこで、Rotationのy軸を-180にしておきます。そこに建物に太陽光のシュミレートをするためのDirectionalLightをもう一つ追加します。建物に影を落としたい場合は、こちらのLightをアニメーションさせます。詳しい記事はこちらUnity5の世界を暗黒の闇で覆う方法 - テラシュールブログ

冒頭のラインが流れるエフェクトは、iTweenPathでパスを作り、そのパスに沿わせたTailRendererです。 紫色に変化した後からラストのサビにかけてもこのラインエフェクトを使用しています。このパスを制作するのがとにかくごり押しで、ひたすら座標をポチポチして作ります。しかもベジエを描くパスなので、直線に曲がることができません。これも同じ頂点を続けるというごり押しで解決。 スライド3 この波紋が広がるシーンはSonarFXを使用。今回かなり助けられたエフェクトです。
壁からボコボコと出ては引っ込む箱はスクリプト制御でまとめて動かしています。 今回、プロジェクションマッピングをするにあたって「偽物の立体を表現をする」という表現をどうしてもしたくてやりました。 スライド4

スライド5
この色なしの状態から汚れた色をつけるシーンはシェーダを書いて実現しました。 スライド6
こちらがシェーダのプロパティ。
ベーステクスチャ+3枚の汚れテクスチャを適応できます。Amountを0から1.5にすることで真っ白→最大汚れに変化、Amount2を0から1.5にすることで最大汚れ→dustLevelで指定した汚れに変化します。このdustlevelはサーバから送られてきた値をオブジェクトごとに設定します。

IMG_3698

IMG_3704

最後はモデルを非表示にすると同時にパーティクルを散らします。パーティクルが複雑な立体状で飛び散るのは予想以上に良い演出になりました。 IMG_3650

プロジェクター台

IMG_3608
機械電気、土木建築の学生がプロジェクター台を制作してくれました。 IMG_3504
真ん中にはパソコンを置くこともできます。 _IMG_3696 IMG_3525

プロジェクションマッピング

_IMG_3526
Unity上のカメラを現実のプロジェクターの位置にざっくり合わせます。プロジェクターが4台なので、それぞれカメラを用意。ここでできるだけ正確に配置すると良いのでしょうが、計測とか一切していません。
ちなみに、プロジェクターは学校の先生からお借りした機種もスペックもバラバラのものを用いています。短い距離でも広い範囲を映し出すことができること、解像度がある程度高いことが必要だと思います。
メインカメラ
main
セカンドカメラ
second
サードカメラ
third
フォースカメ
fourth

これを、一つのプログラムに収めるために起動時に初期化シーンを用意しました。そのプロジェクターに対応するカメラを選び、サーバのIPとポートを指定して起動するようにします。 スライド7

Unity→Syphon→MWServerといった感じで映像を出力します。これはまた別の記事に起こそうかなと思います。長くなるのと、僕はWindowsの環境なので。
手順だけ書くと、Unityにコンポーネント付ける、MEServerの入力元にSyphonを指定しUnityのカメラを選択、textureで描画するテクスチャ範囲を指定、mainで投影物にマッピングする、です。
Syphonの導入方法はこちらも参考にしてみてください - ProjectionMappinig - Unityでインタラクティブなプロジェクションマッピングを - Qiita
スライド9
注意点は、Syphonが使えないのでMacじゃないと駄目ということ、MWServer関連の記事が全くないことです。 このMWServerはとても有能で、投影する対象が動かない限りはこのソフトで事足ります。ただ、説明をしているのが公式サイトのみです。もちろん、日本語の資料なんてありません。しかも、公式のチュートリアルでさえかなり説明不足だと感じました。この辺は触りながら体得していきました。というか、主にMacbookPro使ってた後輩くん、記事書いてくれないかな...。

ロボットとのコラボレーション

プロジェクションマッピングされた模型の中を馬車が走ります。この馬車はゲームブースで集まった花を映像ブースまで運んできてくれているという設定。土木建築工学科が制作した高架橋からスタートし、元の場所まで戻ります。このロボット、一つのモーターで4足歩行します。しかも、センサーで自動で進んでくれる。とにかくすごい。 IMG_3669 スライド8 IMG_3670

最後に

技術的なポイントに絞って感想を言うと、Unityのポテンシャルの高さを感じました。僕はWindows7, 後輩はMacOSでしたが、滞りなくマージしたり、Win側でMacバイナリ吐いたりできるのは強かったです。唯一、シェーダ周りだけがWinで動いてMacで変になることはありましたが。