Factory)でのグラフィック処理の解説をしていきます!(全4回予定で今回は2回目)前回は「フロー編」ということでグラフィック処理全体の流れに着目していきました。今回は「グラフィック効果編」ということで、本作における個々の細かな表現がどの様に処理されているかを紹介していきます。※去る2016/4/5にもUnite2016にて一部公開致しましたが、本編はそれも含めつつの内容になっています。そちらの資料と動画も合わせてご覧いただくのをオススメします。「いけにえと雪のセツナ」自体の紹介は割愛致します。(公式HPを是非ご覧ください。)で、前回も触れましたが今回で特に大きくなるポイントを挙げていきます。それは「見下ろし型のRPGである」という点です。ゲームの大半は上の様なカメラ配置となってます。(イベント的に、たまに水平方向寄りのカメラになることはあります。)あとは大きなポイントとして、「カメラが回り込まない」ということも挙げられます。それらを踏まえ、グラフィック効果を取り上げていきます。リアルタイムシャドウ(影)本作では雪の表現以外に、影の表現も大事にしてきました。プログラム的に気を付けたのは以下の2つです。・影のぼかしを綺麗なものにする。・描画負荷をなるべく軽くする!背景の影に関してはUnite2016でもお話ししました。今回はリアルタイムシャドウ、つまりキャラクターなどの動くものが落とす影について技法を説明します。UnityのシャドウシステムについてUnity自体にも標準でシャドウを落とす機能が搭載されています。本作ではそれを採用することを見送りました。大きな理由として、やはり描画負荷的に不安なのが一番でした。シャドウの処理は最近では「デプスシャドウ」形式が一般的です。深度値が書かれたシャドウマップを作成し、デプスを比較して厳密にシャドウ処理を行うもので、Unityでもその形式が基本です。その形式だとぼかし等を行う時に高負荷になりがちです。いうことで、代替の手段を探す必要があります。丸影等も検討しましたが、最終的に本作は「投影テクスチャシャドウ」を採用しました。「投影テクスチャシャドウ」はデプスシャドウと異なり深度値を利用せず、モデルにただ単純にシャドウマップを貼り付けるという手法です。デプスシャドウと異なり深度比較しなくて良いのは大きな利点ですが、深度比較を行わないということは、不用意に壁などにも影が投影されてしまうということもあります。本作は見下ろし型が画面の基本構成で、床や地面を見る機会が非常に多いです。床や地面にキチッと影が映れば、ゲームとしての目的は大部分は達成していると言えます。いうことで本作では原則として、「床以外は影を落とさない」とすることで通しました。セフルシャドウもありません。下画像はその例で、箱や壁などに影が落ちていないことが分かります。(分かりやすくするために影色を赤にしてあります。一部制限のあるテクスチャシャドウですが、軽量化が見込める他にも以下の効果が得られました。・影のぼかしが簡単に行える。・半透明対応が綺麗に行える。本作での実際のゲーム画面と、その時のシャドウマップが以下になります。シャドウマップシャドウマップは原則として「RGB565」で持っています。(小さいですが)Rがシャドウ対象部分というのが分かります。投影テクスチャシャドウはシャドウマップのカラーを参照するだけなので、シャドウマップを予めぼかしておけば、影も自然とぼけるようになります。(物理的に正しいかどうかはこの際無視しています。シャドウマップ(一部表示)ぼかし無しぼかし有り更に、シャドウマップに描き込むカラーを薄くすることで、半透明影の実現も可能にしました。Max」で描くのがポイントです。シャドウマップ(一部表示)アルファ=1.0アルファ=0.5この様に投影テクスチャシャドウ化することで、低コストで面白い効果を得ることが可能になりました。背景影との馴染み背景の影はUnityでのEnlightenの焼き付けになります。(詳細はUnite2016の資料を参照下さい。)本作に関わらず背景の影とリアルタイムの影の「馴染み」というのは非常に繊細な問題です。本作でもそれは(ある程度は)回避することが出来ました。「馴染み」から少し外れますが、「影の色を付けたい」という要望がアーティスト側から上がってきました。本作ではシェーダは基本的にUnityのStandardシェーダの改造なのですが、以下の関数周りを操作しています。bakedColorTex)これは影とライトマップを馴染ませる処理で、attenuationが影率(0〜1)が入りますが、本作では改造して1要素→3要素(half3)に変更してあります。これで影色の制御とともに、馴染みも解決しました。(影がだいぶ薄くないと馴染みにくいですが。Vita固有の対処を挙げます。シェーダ内では以下の関数でライトマップカラーを取得します。Vitaに限り、ライトマップのsqrt値を計算します。Vitaではライトマップテクスチャ自体をインポート時にsqrt計算しています。低層&高層シャドウは原則として床や地面のみに落ちるとしていますが、それだとやはり物足りなさを感じます。そこで、シャドウマップ自体は「RGB565」にしていて今まではRだけ使っていましたが、Gチャンネルも活用し、表現を一つ追加しました。G=高層向け」という扱いです。ワールドマップでは、キャラクターは「低層」、雲は「高層」としています。シャドウマップこれの使い分けですが、影を受ける側のシェーダを以下の様に設定できます。2低層、高層の両方ともの影を受ける。3高層のみの影を受ける。床や地面は2にしてあります。プレイヤーなどのキャラクターは殆ど3です。することでプレイヤー等が雲などの影を受けるという効果も得られます。(下の画像は分かりやすくするため、影を濃くしています。↓(雲の影に入る)↓リアルタイムリフレクション本作はやはり雪の表現が一番目につくところかと思いますが、他に力を入れた点として、「写り込み(リフレクション)」があります。それは例えば村での水面であったり、氷系の洞窟だったりします。動的に動くものも写り込みの対象となる仕様でした。本章ではリアルタイムでのリフレクションについてどう実装を行ったかを説明します。(※注・本章での画像の写り込みは、目立たせる為に意図的に濃くしています。)Unityでのリフレクションシャドウと同じく、こちらもUnityでの標準の写り込みについて検討しました。候補は2つありました。・スクリーンスペースリフレクション(SSR)を導入する。Probesを利用する。結論から言うと、両方共導入を見送りました。その理由はそれぞれ以下の通りです。★SSR・公式のものが当時はまだ用意されていなかった。Storeで有志のを購入しても良かったが・・・)・ポスト処理で、デプステクスチャが必要になってしまう。(無駄なレンダリングが走る)・何より、高負荷になりそう。Probes・導入に関しての勉強時間が多く取られそう。・ゲーム性から、機能過多と感じたため。Probesは立方体処理で合わなそうと判断し見送りました。いうことで、リフレクションもシャドウと同様、独自実装の道を走ります。本作は「見下ろし型」という特徴以外に、「地面がほぼ平らである」というのが挙げられます。具体例として、「モルの港」を挙げて説明します。こちらは港だけあり、海に接した村という大きな特徴があります。水面にはキャラクターや背景が写り込んでいます。写り込みはこの水面(海)だけですが、これは1枚の板でモデルが構成されています。以上を踏まえての解説ですが、本作のリフレクションはこの特性を利用しています。原理は以下の通りになります。・ゲーム時のカメラを写り込みする面に対して反転し、それをリフレクションカメラとする。・リフレクションカメラから捉えた映像を、写り込みするものだけ「写り込み用バッファ」に描く。(バッファはブレンドバッファ法で貼れるように作成)(RGB)(A)・水面をメインバッファに描画する際、上記バッファをテクスチャとして貼り付けて描画。(水面感を出すため、法線マップを利用して適当に歪ませる)・あとは普通にモデル等を描画して終了。凹凸面へのリフレクションと異なり平面の場合はこの方法で対応でき、処理工程も必要最低限なので、負荷の面もクリアになりました。複雑な地形のリフレクション平面の場合は今ご紹介した方法で対応が可能です。マップによっては、高低差だったり坂道のあるマップも存在します。下の「カッチの洞窟」などはその典型と言えます。「低い床」「高い床」「坂道」でそれぞれ反射のされ方が異なると思いますが、この場合の対策はかなり強引で、「写り込む平面全てにリフレクションカメラを持つ」ことで対処しています。全てのリフレクションカメラを使い、写り込み用バッファを構築していきます。(最中、マスク用途でステンシルなどを利用。)単一の平面に比べ、純粋にカメラの数が増えていますので、処理量もそれに比例して増加します!ただ、洞窟全体だと平面の種類がさらに多くなるのですが、それはゲームカメラから判断し、そのフレームでのリフレクションカメラの個数をなるべく少なくするようにはしています。これで本作におけるリフレクション処理が完成しました。マップ内アニメーション本作では比較的草や木の生えているマップが多いのですが、そういったものが定期的に揺れたり、時には木からの落雪などが発生したりします。いったアニメーションをどのように実現したかを説明していきます。GPUUnityでの実現を考えるとAnimatorの利用を真っ先に思い付くと思います。マップによっては木や草が非常に多く、そのスキニングコストも割と不安でした。Fetch)を用いてアニメーションを実現しています。FetchVTFとは、頂点シェーダでテクスチャを参照する機能です。頂点シェーダでは頂点をある程度操作して形状の変更も行えるのですが、テクスチャを参照してそれをより発展的に利用出来るための仕組みです。(Unite2016でも雪面の凹みを実現するためにVTFを利用した話をしました。)基本の処理イメージとしては「0〜255の階調のテクスチャを作成(サイズは1×1テクスチャ)」「それを頂点シェーダで読み取り、任意の方向に動かす」というものです。定期的なアニメーションはこのテクスチャを常に更新し続けることで行えます。(例えば「0→255→0→255・・・」と繰り返すと揺れる感じが出る。上記の様にテクスチャを利用したアニメーションはスキニング負荷が減って良さそうですが、木や草毎にテクスチャを作成して利用した場合、マテリアルもバラバラに管理することになり、それはそれで非効率になってしまいます。いうことでそれを見直すため、処理をある程度まとめるなどの処理を行ってます。Hierarchyをベースに、その総合アニメ処理の流れを見てみましょう。「MapVertexAnimator」というのが自作した処理です。一つ一つの木に対してはアニメーション処理を行わず、あくまでこの「MapVertexAnimator」を起点にしています。こちらでアニメーションするテクスチャをバラバラに作るのではなく、一つにまとめて作成します。その際、「アニメーションカーブ」を参照します。(現在時刻でのカーブの値(-1〜1)を取得し、それをテクスチャ化する。)木はそのテクスチャを参照して頂点を動かしますが、テクスチャがひとまとめになっているので、木毎にマテリアルを分けておく必要がなくなりました。ランダムアニメ再生MapVertexAnimatorで処理をひとまとめにしたものの、それぞれの木々はランダムに再生していてほしいものです。それももちろん対応しており、以下のフローで実現されます。・それぞれの木や草に対し、0〜15のIDを割り振る。Vita以外は0〜63)・MapVertexAnimatorの「アニメーションカーブ」から適当に16点獲得し、16×1サイズのテクスチャに横詰めで配置作成する。Vita以外は16→64)・木や草のシェーダ側ではIDに応じたテクスチャ座標でテクスチャを参照し、頂点を動かす。この流れでそれぞれの木や草などにランダム性を持たせられます。Vita以外は64)に限定しているところで、割り振られるIDによっては同じ動きをする木が複数存在します。それぞれの木に全く異なるIDを振ると数百にも及ぶことがあり、テクスチャサイズも大きく、そして更新も重くなるので16で絞りました。16くらいなら同一の動きをしているものがあるのが気付かれにくいです。本作の感想としてよく見かけるのが、「木からの落雪が良い」というものでした。こちらもMapVertexAnimatorで実現しています。・Hierarchyで、エフェクト処理(落雪エフェクト)を子に用意。(下図の場合、「mv_tree_anim_eff_lo」がMapVertexAnimator、「eff_macom_0003_01_01」が落雪エフェクト)・MapVertexAnimatorの「エフェクト発生時刻」がエフェクト発生タイミングで、「アニメーションカーブ」がそこを越えたときにエフェクト(eff_macom_0003_01_01)を発生させます。非常にシンプルな作りではありますが、これで任意のエフェクトをアニメーションついでに呼ぶことが可能になっています。まとめ今回は「いけにえと雪のセツナ」におけるグラフィック効果の話をしていきました。シャドウ、リフレクション、マップアニメーションで、意図したわけではないのですが最終的にUnityでの標準機能に頼らない設計になってしまいました。これは本作のゲーム性的にその判断をしたので、別の機会ではUnityの標準機能を利用することもあると思いますし、紹介したところ以外ではUnityの機能を活用したところもあります。その辺りの見極めはゲームエンジン利用スキルとは別と思いますので、磨くように意識すると良いかなと考えます。次回は「シェーダ編」をお送りする予定です。Factoryは一切の責任を負いません。株式会社ロジカルビートコメントは停止中です。