続・ユニティちゃんを超ARしたときの話

(※こちらは過去の記事を移転したものです)

第8回ニコニコ学会βシンポジウム「研究してみたマッドネス」にて超ARについて発表する機会をいただいたので、話題のUnity5で実装してみることにしました。

(超ARとはWebカメラを使ったMovie-Based Lighting技術で、実空間の光の情報をキャラクタに反映できる新しいタイプのARです。普通のARでは味わえない「こっち出てきた感」を演出することができます。詳細はこちら。)

Unity5版は動画だとこんな感じです。

前書いた超ARはUnity 4.6 proを使っていたので、有料ライセンスがないと動かせませんでしたが、Unity5対応により無料で動かせるようになります!

また、単純に移植したのではなく、Unity5で使えるようになったイメージベースドライティング(IBL)やグローバルイルミネーション(GI)の機能を利用した事で、簡潔に組めるようになり、スピードも絵のクオリティも上がっています。

実装環境

  • Qualcomm Vufolia Unity Extention v4.0.105
  • Unity 5.0.1 Personal
  • MacBook Pro Retina 2012

VuforiaのUnity5のmac 64bit版がリリースされたのはほんとつい最近です。
リリースされて速攻でこれを作りました。
超会議に間に合ってよかった。。

前回との違い

最大の変更点としては、前回はLuxというIBL用の無料ライブラリを使っていましたが、今回はこれは使わずStandard Shaderだけで動かします。

デフォルトのStandard Shader自体にIBLの機能が備わったのでこれで代用可能です。
自前でブラー処理とかやらなくて済むのはかなり大きい

また、Unity5ではリアルタイムGI機能としてReflection Probesという機能が追加されており、これを使うと前回自前実装した部分の大半をまんま代用できるっぽいので代用します。

Refrection Probesとは何かというと、ざっくりいうと、スペキュラ成分専用のLight Probeです。
これをシーンのどっかに置くと、そこから見たシーンのスペキュラ反射を他のオブジェクトに反映させることができる優れものです。

ということで、これらを使うと、前回の実装フローでは

  • A1. 仮装視点をどこかに設置して、そこにWebカメラの映像を写して、そこからCubeMapを作る(要実装)
  • A2. 作ったCubeMapをDiffuse用CubeMapとSpecular用CubeMapにブラー加工する(要実装)
  • A3. Luxに渡してレンダリングする(要実装)

という工程だったのに対し、Unity5版では

  • B1. ReflectionProbeをどこかに設置して、そこにWebカメラの映像を写して、そこからCubeMapも作る(要実装)
  • B2. 作ったCubeMapをライトが参照しているSkyboxに渡す(デフォルトでOK)

というように、実装工数をかなり簡略化させることができます。

CubeMapにレンダリング結果を書き込む方法

今回追加で実装したのは、B2の「ReflectionProbeをどこかに設置して、そこにWebカメラの映像を写して、そこからCubeMapを作る」にあたる部分ですが、その際に問題になるのが、”CubeMapをScriptに渡して、レンダリング結果を反映させて、他のObjectでもそのCubeMapを使い回す方法”です。(任意の視点で周囲のシーンをテクスチャに書き込み、CubeMapにセットする方法については前に述べたので、それについては今回はスコープから外します。)

これを実現するためにscriptに渡すPublic引数は以下の2点です。

public Cubemap targetCubemap;
public Material targetMaterial;

targetCubemapには空のCubeMapを渡し、これにレンダリング結果を書き込みます。
この際に、レンダリングするためのメッシュとマテリアルが無いとレンダリング出来ないため、Materialが必要になります。
また、このtargetMaterialには周囲のシーンを映り込ませるため、反射するシェーダを持たせる必要があります。
今回は「Legacy Shader/Reflective/Diffuse」シェーダをもたせてscriptに渡しました。

そして、以下のようにtargetMaterialにtargetCubemapを設定させる処理をUpdate関数内で呼んでやります。

void Update() { 
    if (isUpdate) { // 毎フレ描画は重いので、良い感じに間引く
         renderCubeMap(); // 周囲のシーンをテクスチャに書き込む処理
        targetMaterial.SetTexture("_Cube", targetCubemap);
        GetComponent<Renderer>().material = targetMaterial;
    }
}

スクリプト側で変更するのはこれらの一連の処理のみです。

あとはメッシュの情報が無いとエラーが出ると思うので、このscriptを持たせるGameObjectには必ずMesh Rendererコンポーネントを追加して、ElementにtargetMaterialを設定しておいてください。

script

自分は以下の図のように、シーンのどこかにWEBカメラの映像を投影したReflectionDomeオブジェクトを設定しているので、この仮装視点の位置にReflection Probeを置き、そのReflection ProbeにこのScriptを貼っています。

cam

あとは、適当にDirectional Lightをセットして、targetCubemapから作ったSkyBoxを設定すれば、動画のような環境光が反映されたMovie-Based Lightingが実現できます。

所感

前回の実装はブラーが汚かったし、CPU実装で重かったので、デフォルトで任せられるようになったのでパフォーマンスもクオリティも上がった気がします。

あと、Unity5をいじって思ったことなんですが、StandardShaderに慣れていないだけかもしれないですが、なんかラップライティングっぽい輪郭が出ちゃってるのは気のせいでしょうか。
パラメータもよくわかんなくて、ライト切っててもアンビエント強すぎたりするし、ちゃんとshaderのコードみないとかもですね。

最後に、こんなのも作りました。

coil