ARCoreで3Dプリント風エフェクトを実現する〜呪文による積層造形映像制作の舞台裏〜
2018/04/04
  • このエントリーをはてなブックマークに追加

はじめに

カブクの甘いもの担当の高橋憲一です。

2018年の4月1日(エイプリルフール)に「音声ユーザーインターフェースを用いた新方式積層造形装置の提案」という記事をカブクのdeveloperブログで公開しました。その最後に呪文を唱えることによりモンスターを積層造形する動画が出てくるのですが、これはUnity + ARCoreで作ったAndroid用のARアプリにより実現しています。

この記事では、そのアプリに実装したARで3Dプリント風にオブジェクトが現れるエフェクトをUnityとARCoreで実現する方法について解説します。

実装するにあたって、クリップ面を下から上に動かしていけばできるだろうという見当はついていたのですが、ちょうど3D Printer Shader Effectというタイトルの解説ページを見つけたのでそちらを参考にしました。ただしARCoreを使ったシーンに適用する際にそのままではうまくいかなかった部分もありましたので、日本語での解説も含めてここにまとめておきます。

サンプルシーンに組み込む

解説するにあたり、Googleが提供するARCore SDK for Unityに含まれているサンプルシーンHelloARをビルドして実行できる環境が既にあることを前提に進めます。このサンプルシーンはARCoreで認識した平面を画面上でタップするとドロイドのオブジェクトが表示されるというものなのですが、そのままではオブジェクト出現時のエフェクトはなくパッと現れます。これに3Dプリント(積層造形)風に下から上に徐々に現れるというエフェクトを組み込んでいきます。



指定したY座標のX-Z平面でクリッピングするシェーダーの記述と、そのY座標の値をフレームごとに移動させるスクリプトを組み合わせることで実現しています。
使用したUnityのバージョンは2017.3.0f3、動作検証はGalaxy Note8で行いました。

シェーダーの編集

サンプルに含まれるARCore/DiffuseWithLightEstimationというシェーダー (Assets/GoogleARCore/Examples/HelloARの下のMaterials/Shadersがシェーダーのフォルダで、MobileDiffuseWithLightEstimationがそれです) をコピーし、下記のように編集します。(オリジナルとの差分を見ていただく方が分かりやすいかもしれません)


6〜8行目、22〜24行目:
次の3つのプロパティを追加します。
  • _ConstructY … クリッピングするY座標の値
  • _ConstructGap … 積層していく際に色を変える境界の高さ
  • _ConstructColor … 積層していく際の境界の色
これらのプロパティは描画時のオブジェクトに対して下図のように作用します。

15行目:
通常は描画パフォーマンスを上げるために見えない面(ポリゴンの裏側)は描画しないようしています(カリングON)が、造形途中のポリゴンの裏側を見せるようにするためにCull Offを入れます。
29行目:
組み込みサーフェイスシェーダー変数worldPosをInput構造体に追加して、ピクセルのワールド位置を取得できるようにします。
39〜42行目:
ピクセルのワールド位置のY座標の値が「クリッピングするY座標 + 境界の高さ」より大きい(クリッピングする位置より高い位置にある)場合、discardして描画しないようにします。
44〜48行目:
ピクセルのワールド位置のY座標の値がクリッピングするY座標より小さい場合、通常描画の対象としてテクスチャからの色を反映します。
49〜52行目:
ピクセルのワールド位置のY座標の値がクリッピングするY座標の値以上の場合、境界部分の描画対象として境界の色を反映します。

スクリプトのの追加と編集

ドロイドのオブジェクトは、Unityのエディタ上ではProjectウィンドウのAssets/GoogleARCore/Examples/HelloARの下にPrefabsというフォルダがあり、その配下にAndyという名前のプレハブとして存在しています。これを編集して独自のプレハブとするために、一旦Hierarchyウィンドウにドラッグ&ドロップして、下図のようにAndyPと名前を変更します。


上図のようにAndyPの下の階層を開いてAndy_GEOを選択します。
ここでInspectorウィンドウで [Add Component] → [New Script]の順に選択して新しいスクリプトを追加して、BuildingTimerと名前をつけます。BuildingTimerをダブルクリックしてエディタが開いたら、次のように編集します。


Start () メソッド

画面をタップしてオブジェクトが生成される際に1度実行されます。

15〜16行目:
このスクリプトが関連づけられたオブジェクトのマテリアルを複製して保持します。
マテリアルに設定したシェーダーのプロパティ(クリッピング位置のY座標)を操作するため、そのままでは複数のオブジェクトを表示した場合すべて連動してしまいます(既に表示済みのオブジェクトに対してもエフェクトがかけられることになります)。それを防ぐための処理です。
17〜20行目:
オブジェクトのバウンディングボックスのサイズを取得し、Y座標の変化の範囲として接地面(最小値)から頭のてっぺん(最大値)までを設定します。
注意する必要があるのは、接地面であるARCoreで取得した平面のY座標(高さ方向)です。参考にしたサイトのコードでは接地面はのY座標は0の前提で作られていました。ここでは最小値と最大値にワールド空間でのY方向のオフセット値(transform.position.y、positionはタップしてオブジェクトが配置される際に与えられます)を加えることで高さを合わせてあります。
21行目:
クリッピング位置の初期値(接地面である最小値)をマテリアルに設定することでシェーダーに渡します。
23行目:
オブジェクトの配置時点からエフェクトを開始するために、現在の時間を保存してアニメーションの開始時間とします。

Update () メソッド

オブジェクトの生成後、起動中は毎フレーム実行される部分です。

28行目:
clipY < maxYという条件を入れることで、最大値(頭のてっぺん)に到達したらそれ以降クリップするY座標値は変化しないようにします。
29行目:
線形補間関数(Mathf.Lerp)を使って、クリッピング位置の最小値から最大値の間の経過時間に応じた値を求めます。
30行目:
求めたクリッピング位置の値をシェーダーに渡します。

シェーダーの変更

設定してあるシェーダーを先ほど編集した3DPrint/ARCore/DiffuseWithLightEstimationに変更します。
InspectorウィンドウのAndyMaterialコンポーネントのShaderの右側のメニューを押し、[3DPrint]→[ARCore]の[DiffuseWithLightEstimation]の順に選択します。先ほどシェーダーに追加したConstruct Y, Gap, Colorの各プロパティがInspectorから設定可能になります。

ここまでの作業でAndy_GEOのInspectorウィンドウは下図のようになります。
Building Timer (Script)のDurationや、AndyMaterialのConstruct GapやConstruct Colorは適宜調整してみてください。



プレハブの設定

ここでSceneビューでAndyPが見える状態でUnityのエディタのプレビューを押すと、3Dプリント風のエフェクトの確認ができるはずです。問題ないようなら、AndyPをHierarchyウィンドウからProjectウィンドウのAssetsの下にドラッグ&ドロップして新たにプレハブ化します。HierarchyウィンドウにあるAndyPは削除しておきます。

あとはHierarchyウィンドウでExampleControllerを選択し、InspectorウィンドウのHello AR Controller (Script)コンポーネントの[Andy Android Prefab]に先ほど作成したAndyPプレハブをProjectウィンドウからドラッグ&ドロップすれば完了です。



最後に


ARCoreでオブジェクトを表示するときのエフェクトとして思いの外よい効果を得ることができました。今後の機会でARCoreによるアプリをデモする際にも使っていこうと思います。
カブクでは来年のエイプリルフールネタを一緒に考えてくれるような遊び心を持つエンジニアを募集しております。

P.S.

筆者(高橋)は、5月7日から9日にかけて開催されるUnite Tokyo 2018ARCoreについてのセッションでスピーカーを務めることになりました。サンプルを動かしてみた後にその先へ進む際のヒントになるような話をしたいと思っていますので、良ければそちらの方もよろしくお願いします。