これは Unity #2 Advent Calendar 2017 21日目の記事です。
デモシーン界隈では、美しいCGアニメーションをリアルタイムに生成するプログラムを「デモ」と呼びます。
今回はUnityを使ったデモの制作に初挑戦しました。 13秒の短い無音の動画です。
作品の解説
「レイマーチングで動的に生成したモデル」と「ポリゴンメッシュのモデル」を混在させた作品です。 ロボットは通常の3Dモデルですが、床や柱のモデルはレイマーチングでプロシージャルに生成しました。
レイマーチングにはuRaymarchingというAssetを利用しました。
映像作品と相性が良さそうなので、Unity2017のTimelineも利用しました。
今回は試作という意味から、uRaymarchingとTimelineの他にも様々なアセットを試しました。 色々と試行錯誤をしたので、この記事ではそのノウハウを共有したいと思います。
Unityのバージョンは執筆時点の最新版である2017.2.1f1を用いました。
uRaymarchingによるレイマーチング
uRaymarchingはレイマーチングのシェーダの作成をサポートするシェーダ群とエディタ拡張です。 開発者は@hecomiさんです。
uRaymarchingではDefferedシェーディングを採用しており、レイマーチングのシェーダはGバッファに対して結果を書き込みます。 そのため、今回のようにレイマーチングとポリゴンのモデルが混在したシーンであっても、一貫したシェーディングを実現できました!
レイマーチングの衝突判定などの共通処理は、uRaymarchingが提供する共通のシェーダが肩代わりしてくれます。 uRaymarchingの利用者は、レイマーチングの距離関数の定義とGバッファの書き込みの処理だけに集中できるため、開発効率が向上しました。
具体的な使い方を簡単に説明しますと、まずはuRaymarchingのエディタ拡張でレイマーチングシェーダの雛形を作成します。 次に雛形シェーダの2つの関数をカスタマイズすれば、独自のレイマーチングシェーダをお手軽に作成できました。
- 距離関数の定義を
DistanceFunction
関数に記述 - Gバッファへの書き込み処理を
PostEffect
関数に記述
uRaymarchingのチュートリアル動画です。
距離関数の設計
Boxの組み合わせだけでシーンを構成しました。 床はBoxを敷き詰めているのは見た目通りだと思いますが、柱もBoxの組み合わせで作っています!
距離関数のfoldの記事で紹介した回転のfoldを利用して、 Boxから上から見たときに多角形になる柱を生成しました。
単純なBoxの形状だけでも、時間経過で形状が変化する面白い形ができたと思っています。
床がランダムな順番に光る演出
床をランダムに光らせる演出は自分でも気に入っています。
この演出はお手軽な方法で実装できました!
まずは、Y座標に応じて光るように、Gバッファに書き出すemissionを設定します。 光らせるY座標の位置は時間でアニメーションさせます。
float4 _SlideEmission;
inline void PostEffect(RaymarchInfo ray, inout PostEffectOutput o)
{
float a = frac(4.0 * ray.endPos.y - 2.0 * _Time.x - 0.5);
float width = 0.04;
o.emission = _SlideEmission * abs(sin(PI * 12.0 * _Time.x)) * step(a, width) * ((a + 0.5 * width) / width);
}
次に、床のブロックの高さの動きをランダムに設定します。
float dFloor(float3 pos)
{
float3 p = pos;
p.xz = Repeat(p.xz, 0.5);
p.y += 1 + 0.1 * sin(36.0 * _Time.x + 2.0 * Rand(floor(2.0 * pos.xz)));
return sdBox(p, float3(0.2, 0.2, 0.2));
}
これだけで、床をにランダムに光らせる演出の完成です!
床の高さがバラバラになっているので、等高線で光らせるとタイミングが微妙にずれて、いい感じにバラバラのタイミングで光ります!
uRaymarchingのトラブルシューティング
2点だけつまずいたポイントがあったので、後学のためにメモを残しておきます。
- ShaderTemplates で Direct GBuffer を選択すると、影が落ちない(Shadow Caster が動作しない)
- これはUnityの仕様に原因があるらしく、hecomiさんの記事にも書いてありました
- プロジェクトの設定で
metalEditorSupport: 0
にしないと、Macで動作しない
Timelineとの連携
Unity2017のTimeline機能でカメラワークやロボットの動き、UI上のテキストなどの演出の制御をしました。
カメラワーク
Timelineからゲームオブジェクトを操作する2つの方法があります。
Playables
を実装・利用する方法- APIは複雑で、気軽に使うのは難しい
- 作り込めば何でもできる
ITimeControl
を継承したコンポーネントを実装・利用する方法- APIは単純で、気軽に使える
- できることが少ない(クリップの現在時間しか受け取れない)
カメラワークの制御をどちらで行うのか悩みましたが、最終的には以下の理由でITimeControl
に決めました。
- とりあえずカメラを動かすだけなら、
ITimeControl
が手っ取り早いと感じた - カメラワークを汎用的な
Playables
に落とし込む時間もスキルも足りなかった
とりあえずカメラワークをITimeControl
で実装することはできましたが、
ITimeControl
では再生時間の情報しか受け取れず、クリップごとにパラメータを持たすことすらできません。
三角関数などを駆使してカメラのtransformを操作して、無理やりカメラワークを実装しましたが、
職人芸すぎてメンテナンスが困難なコードになりました。
今回は満足するものはできなかったので、次回はこれらの方法でカメラワークに再挑戦したいです。
- Default Playables
に含まれているTimeline Playable Wizardを使えば、
Playables
の雛形コードを作成できるそうなので、これを利用して独自Playables
の実装に再挑戦する - CinemachineというUnity公式のカメラワークを作るための
Playables
を利用する
ロボットの動き
ロボットにAnimator
コンポーネントをアタッチすれば、普通にAnimation Track
でクリップを再生できました。
2つのAnimation Track
のクリップを重ねるように配置すると、モーションのブレンドができるので、
待機モーションから走るモーションへのブレンドはこれを利用しました。
Animation Track
の中に作成できるOverride Track
でキャラクターの移動を実現しました。
Override Track
ではパラメータのアニメーションのカーブを直接編集できます。
テキスト
後半のタイトル文字にはTextMesh Proを使用しました。
Timelineとの連携は、Activation Trackを使って実現しています。
1文字ずつ表示する部分は、TextMesh ProのサンプルのTextConsoleSimulatorクラスを使って制御しています。
しかし、このクラスはTimelineは考慮されておらず、通常再生時と録画時とで表示速度がずれる問題が残りました。
将来的には独自のPlayables
を実装して、これらの問題を解決したいです。
パーティクル
ロボットの足元の火花には、Unity Particle Pack に含まれている「ElectricalSparksEffect」を使用しました。 Timelineとの連携は、Activation Trackを使って実現しています。
その他
ポストエフェクト
ポストエフェクトには、Post-processing Stack v2を利用しました。
以下のポストエフェクトを利用しました。
- Fog
- レイマーチングでは遠景にエイリアシングが発生して汚くなる弱点があるので、Fogで遠景を暗くしました
- 現実でも距離の二乗に比例して光が減衰するので、Fogで現実感が増します
- Bloom
- Bloomは明るい光源からの光が周囲に漏れるように見える効果です
- 今回はemissionを多用したシーンなので、Bloomが効果的に機能しました
- Ambient Occlusion
- AOで大域照明感を出しました
- 暗いシーンなので、違いは分かりにくいかもしれませんね
ポストエフェクトの有無で比較画像を用意しました。 左がポストエフェクトOFF、右がポストエフェクトONです。 違いが一目瞭然ですね!
3D素材
ロボットの3DモデルはSpace Robot Kyleを使わせていただきました。
モーションはユニティちゃん 3Dモデルデータを利用しました。
動画撮影
冒頭のYouTubeの動画は、Unity Recorderを使って撮影しました。 このアセットは、Unityの画面を録画し、動画として保存してくれます。 固定フレームレートに対応しているので、非力なPCでも撮影が可能です。
Unity Recorderには、Timelineとの連携機能もありました。 Recorder trackをタイムラインに追加すると、エディター再生時に自動で録画ができます。
注意点として、v0.1ではUIが録画できないという不具合がありました。 GitHubのReleasesから、v0.2(現時点の最新版)をダウンロードすることで解決できました。
感想
Unityを上手に利用すれば効率的にデモ作成できると感じました。
- 便利なAssetがたくさん提供されている
- リアルタイムに見た目を確認しながら、シェーダのパラメータを調整できる
- シェーダ(ShaderLab)に数行コードを足すだけで、インスペクタにパラメータを露出できる
- リアルタイムに見た目を確認しながら、シェーダやスクリプトを編集できる
- Unityに標準搭載されているホットリロード機能によって、シーンの再生中でもシェーダやスクリプトの変更が反映できる
ソースコード
UnityのプロジェクトをGitHubで公開しています。スターが欲しいです。
今回のデモ用のファイルはAssets/Demoscene/TheGlow
のディレクトリにあります。