「いけにえと雪のセツナ」グラフィック解説(第2回・グラフィック効果編)

By | 2016年7月25日

はじめに

前回に引き続き、「いけにえと雪のセツナ(I am Setsuna)」(Tokyo RPG Factory)でのグラフィック処理の解説をしていきます!(全4回予定で今回は2回目)

前回は「フロー編」ということでグラフィック処理全体の流れに着目していきました。
今回は「グラフィック効果編」ということで、本作における個々の細かな表現がどの様に処理されているかを紹介していきます。

※去る2016/4/5にもUnite2016にて一部公開致しましたが、本編はそれも含めつつの内容になっています。
そちらの資料動画も合わせてご覧いただくのをオススメします。


大前提

「いけにえと雪のセツナ」自体の紹介は割愛致します。
公式HPを是非ご覧ください。)

で、前回も触れましたが今回で特に大きくなるポイントを挙げていきます。
それは「見下ろし型のRPGである」という点です。

setsuna_scene_camera

ゲームの大半は上の様なカメラ配置となってます。
(イベント的に、たまに水平方向寄りのカメラになることはあります。)

あとは大きなポイントとして、「カメラが回り込まない」ということも挙げられます。

それらを踏まえ、グラフィック効果を取り上げていきます。


リアルタイムシャドウ(影)

本作では雪の表現以外に、影の表現も大事にしてきました。
プログラム的に気を付けたのは以下の2つです。

・影のぼかしを綺麗なものにする。
・描画負荷をなるべく軽くする!

背景の影に関してはUnite2016でもお話ししました。
今回はリアルタイムシャドウ、つまりキャラクターなどの動くものが落とす影について技法を説明します。

Unityのシャドウシステムについて

Unity自体にも標準でシャドウを落とす機能が搭載されています。
ですが、本作ではそれを採用することを見送りました。

大きな理由として、やはり描画負荷的に不安なのが一番でした。

シャドウの処理は最近では「デプスシャドウ」形式が一般的です。
深度値が書かれたシャドウマップを作成し、デプスを比較して厳密にシャドウ処理を行うもので、Unityでもその形式が基本です。
ただその形式だとぼかし等を行う時に高負荷になりがちです。

ということで、代替の手段を探す必要があります。

見下ろし型ということを考慮

丸影等も検討しましたが、最終的に本作は「投影テクスチャシャドウ」を採用しました。
「投影テクスチャシャドウ」はデプスシャドウと異なり深度値を利用せず、モデルにただ単純にシャドウマップを貼り付けるという手法です。

setsuna_shadow_map_info_0

デプスシャドウと異なり深度比較しなくて良いのは大きな利点ですが、深度比較を行わないということは、不用意に壁などにも影が投影されてしまうということもあります。

setsuna_shadow_map_info_1

ただ本作は見下ろし型が画面の基本構成で、床や地面を見る機会が非常に多いです。
床や地面にキチッと影が映れば、ゲームとしての目的は大部分は達成していると言えます。

ということで本作では原則として、「床以外は影を落とさない」とすることで通しました。セフルシャドウもありません。
下画像はその例で、箱や壁などに影が落ちていないことが分かります。(分かりやすくするために影色を赤にしてあります。)

setsuna_shadow_no_receive

投影テクスチャシャドウの副次効果

一部制限のあるテクスチャシャドウですが、軽量化が見込める他にも以下の効果が得られました。

・影のぼかしが簡単に行える。
・半透明対応が綺麗に行える。

まず本作での実際のゲーム画面と、その時のシャドウマップが以下になります。

setsuna_shadow_on setsuna_shadow_map_normal
 ゲーム画面 シャドウマップ

シャドウマップは原則として「RGB565」で持っています。
(小さいですが)Rがシャドウ対象部分というのが分かります。

投影テクスチャシャドウはシャドウマップのカラーを参照するだけなので、シャドウマップを予めぼかしておけば、影も自然とぼけるようになります。(物理的に正しいかどうかはこの際無視しています。)

ゲーム画面 シャドウマップ
(一部表示)
ぼかし無し setsuna_shadow_blur_off_color setsuna_shadow_blur_off_SM
ぼかし有り setsuna_shadow_blur_on_color setsuna_shadow_blur_on_SM

更に、シャドウマップに描き込むカラーを薄くすることで、半透明影の実現も可能にしました。
(シャドウマップに描き込む際に「BlendOp Max」で描くのがポイントです。)

ゲーム画面
(影を濃くしています)
シャドウマップ
(一部表示)
アルファ=1.0  setsuna_shadow_alpha100_color setsuna_shadow_alpha100_SM
アルファ=0.5  setsuna_shadow_alpha050_color setsuna_shadow_alpha050_SM

この様に投影テクスチャシャドウ化することで、低コストで面白い効果を得ることが可能になりました。

背景影との馴染み

背景の影はUnityでのEnlightenの焼き付けになります。(詳細はUnite2016の資料を参照下さい。)

本作に関わらず背景の影とリアルタイムの影の「馴染み」というのは非常に繊細な問題です。
本作でもそれは(ある程度は)回避することが出来ました。

setsuna_shadow_blend_before

↓(背景影が暗いところに入る)

setsuna_shadow_blend_after

「馴染み」から少し外れますが、「影の色を付けたい」という要望がアーティスト側から上がってきました。
本作ではシェーダは基本的にUnityのStandardシェーダの改造なのですが、以下の関数周りを操作しています。

inline half3 MixLightmapWithRealtimeAttenuation (half3 lightmapContribution, half attenuation, fixed4 bakedColorTex)

これは影とライトマップを馴染ませる処理で、attenuationが影率(0〜1)が入りますが、本作では改造して1要素→3要素(half3)に変更してあります。
これで影色の制御とともに、馴染みも解決しました。(影がだいぶ薄くないと馴染みにくいですが。)

最後にPS Vita固有の対処を挙げます。
シェーダ内では以下の関数でライトマップカラーを取得します。

inline half3 DecodeLightmapRGBM (half4 data, half4 decodeInstructions)

上記関数内でPS Vitaに限り、ライトマップのsqrt値を計算します
それにより馴染みにくくなっていたので、PS Vitaではライトマップテクスチャ自体をインポート時にsqrt計算しています。

低層&高層

シャドウは原則として床や地面のみに落ちるとしていますが、それだとやはり物足りなさを感じます。

そこで、シャドウマップ自体は「RGB565」にしていて今まではRだけ使っていましたが、Gチャンネルも活用し、表現を一つ追加しました。
「R=低層向け G=高層向け」という扱いです。

例えばワールドマップでは、キャラクターは「低層」、雲は「高層」としています。

setsuna_shadow_layer_color setsuna_shadow_layer_SM
ゲーム画面 シャドウマップ

これの使い分けですが、影を受ける側のシェーダを以下の様に設定できます。

①影を全く受けない。
②低層、高層の両方ともの影を受ける。
③高層のみの影を受ける。

床や地面は②にしてあります。
で、プレイヤーなどのキャラクターは殆ど③です。
こうすることでプレイヤー等が雲などの影を受けるという効果も得られます。
(下の画像は分かりやすくするため、影を濃くしています。)

setsuna_shadow_enter_before

↓(雲の影に入る)

setsuna_shadow_enter_after


リアルタイムリフレクション

本作はやはり雪の表現が一番目につくところかと思いますが、他に力を入れた点として、「写り込み(リフレクション)」があります。

それは例えば村での水面であったり、氷系の洞窟だったりします。

setsuna_reflection_info

更に動的に動くものも写り込みの対象となる仕様でした。

本章ではリアルタイムでのリフレクションについてどう実装を行ったかを説明します。
(※注・本章での画像の写り込みは、目立たせる為に意図的に濃くしています。)

Unityでのリフレクション

シャドウと同じく、こちらもUnityでの標準の写り込みについて検討しました。
候補は2つありました。

・スクリーンスペースリフレクション(SSR)を導入する。
・Unity5からのReflection Probesを利用する。

結論から言うと、両方共導入を見送りました。
その理由はそれぞれ以下の通りです。

★SSR
・公式のものが当時はまだ用意されていなかった。(Asset Storeで有志のを購入しても良かったが・・・)
・ポスト処理で、デプステクスチャが必要になってしまう。(無駄なレンダリングが走る)
・何より、高負荷になりそう。

★Reflection Probes
・導入に関しての勉強時間が多く取られそう。
・ゲーム性から、機能過多と感じたため。

特にReflection Probesは検討をしたのですが、本作のゲーム性の「見下ろし型」を考えると、Reflection Probesは立方体処理で合わなそうと判断し見送りました。

ということで、リフレクションもシャドウと同様、独自実装の道を走ります。

本作での基本処理

本作は「見下ろし型」という特徴以外に、「地面がほぼ平らである」というのが挙げられます。

具体例として、「モルの港」を挙げて説明します。
こちらは港だけあり、海に接した村という大きな特徴があります。
そして水面にはキャラクターや背景が写り込んでいます。

setsuna_reflection_moru

写り込みはこの水面(海)だけですが、これは1枚の板でモデルが構成されています。

setsuna_reflection_sea

以上を踏まえての解説ですが、本作のリフレクションはこの特性を利用しています。
原理は以下の通りになります。

・ゲーム時のカメラを写り込みする面に対して反転し、それをリフレクションカメラとする。

setsuna_reflection_camera_single

・リフレクションカメラから捉えた映像を、写り込みするものだけ「写り込み用バッファ」に描く。
(バッファはブレンドバッファ法で貼れるように作成)

setsuna_reflection_bb_single_color(RGB)
setsuna_reflection_bb_single_alpha(A)

・水面をメインバッファに描画する際、上記バッファをテクスチャとして貼り付けて描画。
 (水面感を出すため、法線マップを利用して適当に歪ませる)

setsuna_reflection_draw_sea

・あとは普通にモデル等を描画して終了。

setsuna_reflection_single

凹凸面へのリフレクションと異なり平面の場合はこの方法で対応でき、処理工程も必要最低限なので、負荷の面もクリアになりました。

やや複雑な地形のリフレクション

平面の場合は今ご紹介した方法で対応が可能です。

しかしマップによっては、高低差だったり坂道のあるマップも存在します。
下の「カッチの洞窟」などはその典型と言えます。

setsuna_reflection_multi

「低い床」「高い床」「坂道」でそれぞれ反射のされ方が異なると思いますが、この場合の対策はかなり強引で、「写り込む平面全てにリフレクションカメラを持つ」ことで対処しています。

setsuna_reflection_multi_floor_0 setsuna_reflection_multi_floor_1 setsuna_reflection_multi_floor_2
低い床 坂道 高い床

全てのリフレクションカメラを使い、写り込み用バッファを構築していきます。
(最中、マスク用途でステンシルなどを利用。)

setsuna_reflection_multi_bb

単一の平面に比べ、純粋にカメラの数が増えていますので、処理量もそれに比例して増加します!
ただ、洞窟全体だと平面の種類がさらに多くなるのですが、それはゲームカメラから判断し、そのフレームでのリフレクションカメラの個数をなるべく少なくするようにはしています。

これで本作におけるリフレクション処理が完成しました。


マップ内アニメーション

本作では比較的草や木の生えているマップが多いのですが、そういったものが定期的に揺れたり、時には木からの落雪などが発生したりします。

setsuna_map_animation_info

そういったアニメーションをどのように実現したかを説明していきます。

CPU or GPU

Unityでの実現を考えるとAnimatorの利用を真っ先に思い付くと思います。
ですがマップによっては木や草が非常に多く、そのスキニングコストも割と不安でした。

setsuna_map_animation_scene

結局はそのコストをCPUが請け負うかGPUで請け負うかの違いになりますが、本作ではVTF(Vertex Texture Fetch)を用いてアニメーションを実現しています。

Vertex Texture Fetch

VTFとは、頂点シェーダでテクスチャを参照する機能です。

頂点シェーダでは頂点をある程度操作して形状の変更も行えるのですが、テクスチャを参照してそれをより発展的に利用出来るための仕組みです。

Unite2016でも雪面の凹みを実現するためにVTFを利用した話をしました。)

setsuna_VTF_snow_field

基本の処理イメージとしては「0〜255の階調のテクスチャを作成(サイズは1×1テクスチャ)」「それを頂点シェーダで読み取り、任意の方向に動かす」というものです。

テクスチャ sestuna_map_animation_tex_000
(0)
sestuna_map_animation_tex_127
(127)
sestuna_map_animation_tex_255
(255)
適応後 sestuna_map_animation_swing_000 sestuna_map_animation_swing_127 sestuna_map_animation_swing_255

定期的なアニメーションはこのテクスチャを常に更新し続けることで行えます。
(例えば「0→255→0→255・・・」と繰り返すと揺れる感じが出る。)

総合的アニメ管理

上記の様にテクスチャを利用したアニメーションはスキニング負荷が減って良さそうですが、木や草毎にテクスチャを作成して利用した場合、マテリアルもバラバラに管理することになり、それはそれで非効率になってしまいます。

ということでそれを見直すため、処理をある程度まとめるなどの処理を行ってます。

Hierarchyをベースに、その総合アニメ処理の流れを見てみましょう。

setsuna_map_animation_hierarchy

「MapVertexAnimator」というのが自作した処理です。

setsuna_map_animation_inspector

一つ一つの木に対してはアニメーション処理を行わず、あくまでこの「MapVertexAnimator」を起点にしています。
こちらでアニメーションするテクスチャをバラバラに作るのではなく、一つにまとめて作成します。

その際、「アニメーションカーブ」を参照します。
(現在時刻でのカーブの値(-1〜1)を取得し、それをテクスチャ化する。)

sestuna_map_animation_curve

木はそのテクスチャを参照して頂点を動かしますが、テクスチャがひとまとめになっているので、木毎にマテリアルを分けておく必要がなくなりました。

ランダムアニメ再生

MapVertexAnimatorで処理をひとまとめにしたものの、それぞれの木々はランダムに再生していてほしいものです。

それももちろん対応しており、以下のフローで実現されます。

・それぞれの木や草に対し、0〜15のIDを割り振る。(PS Vita以外は0〜63)

setsuna_map_animation_id_random

・MapVertexAnimatorの「アニメーションカーブ」から適当に16点獲得し、16×1サイズのテクスチャに横詰めで配置作成する。(PS Vita以外は16→64)

sestuna_map_animation_id_tex

・木や草のシェーダ側ではIDに応じたテクスチャ座標でテクスチャを参照し、頂点を動かす。

この流れでそれぞれの木や草などにランダム性を持たせられます。

ポイントは16種類(PS Vita以外は64)に限定しているところで、割り振られるIDによっては同じ動きをする木が複数存在します。

が、それぞれの木に全く異なるIDを振ると数百にも及ぶことがあり、テクスチャサイズも大きく、そして更新も重くなるので16で絞りました。
16くらいなら同一の動きをしているものがあるのが気付かれにくいです。

エフェクト発生

本作の感想としてよく見かけるのが、「木からの落雪が良い」というものでした。

setsuna_map_animation_snowdrop

こちらもMapVertexAnimatorで実現しています。
以下の流れです。

・Hierarchyで、エフェクト処理(落雪エフェクト)を子に用意。
 (下図の場合、「mv_tree_anim_eff_lo」がMapVertexAnimator、「eff_macom_0003_01_01」が落雪エフェクト)

setsuna_map_animation_hierarchy_effect

・MapVertexAnimatorの「エフェクト発生時刻」がエフェクト発生タイミングで、「アニメーションカーブ」がそこを越えたときにエフェクト(eff_macom_0003_01_01)を発生させます。

setsuna_map_animation_call_effect

非常にシンプルな作りではありますが、これで任意のエフェクトをアニメーションついでに呼ぶことが可能になっています。


まとめ

今回は「いけにえと雪のセツナ」におけるグラフィック効果の話をしていきました。

シャドウ、リフレクション、マップアニメーションで、意図したわけではないのですが最終的にUnityでの標準機能に頼らない設計になってしまいました。
ただこれは本作のゲーム性的にその判断をしたので、別の機会ではUnityの標準機能を利用することもあると思いますし、紹介したところ以外ではUnityの機能を活用したところもあります。

その辺りの見極めはゲームエンジン利用スキルとは別と思いますので、磨くように意識すると良いかなと考えます。

次回は「シェーダ編」をお送りする予定です。
お楽しみに!


※お知らせ

株式会社ロジカルビートでは、一緒に働いてくれる仲間を募集しています!
興味を持たれた方は採用ページを是非ご覧下さい!


 

 

【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートならびに株式会社Tokyo RPG Factoryは一切の責任を負いません。

※「いけにえと雪のセツナ」:©2016 Tokyo RPG Factory Co., Ltd. All rights reserved.