『呪術廻戦 ファントムパレード』ストーリー制作を支えるグラフィックシステム

この記事は、2024年3月7日に開催された「CyberAgent Game Conference 2024(CAGC 2024)」のセッション内容をAIによる自動文字起こしをベースに加筆修正したものになります。

セッション概要

『呪術廻戦 ファントムパレード』のストーリー制作において重要な役割を果たした表現技法について解説します。
陰影の表現、Live2Dに対するライティングによってキャラクターはより魅力的になり、背景にも馴染みやすくなりました。
レイヤ指定ポストエフェクトでは、疑似的な被写界深度や戦闘シーンのアニメ風な効果を作り出しています。
これらの技術がどのように活用されたか具体的な事例を交えてご紹介します。

※セッションのアーカイブ動画

登壇内容

自己紹介

2015年新卒でサイバーエージェントに入社、ゲーム事業にていくつかの新規開発を担当しました。
『呪術廻戦 ファントムパレード』では、クライアントのテックリードとして開発に従事し、特に演出周りの仕組みを多く担当しています。

ストーリーパートの紹介

それでは本発表で注目するストーリーパートについて紹介します。

ファンパレのストーリーパートは大きく2種類あります。
Live2Dによる会話劇 (左)、それからより動きのある表現が可能なシネマティックシーン (右) です。

この発表では『呪術廻戦 ファントムパレード』のストーリー制作を行う上で、鍵となったグラフィックシステムについて、作例を交えながら下記の4つに関して紹介していこうと思います。

  1. 陰影/光の表現について
  2. キャラライティングについて
  3. レイヤー指定のポストエフェクトについて
  4. Live2Dの半透明描画に関するチューニングについて

陰影/光の表現

では1つ目の陰影、光の表現についてです。

こちらは、釘崎野薔薇の共鳴りの呪力の光が、手前のわら人形のところにも当たっているような表現になっています。

こちらは、左上の光が入っているところから、各キャラに影ができています。
例えば、手前の竜胆サキの背中の部分はかなり暗めになっていますが、一番左の虎杖悠仁は少し明るめになっており、それぞれに対して影が設定されています。

こちらは、背景の白が漏れてきてキャラクターにかかっているような、光が漏れてくるような表現をしています。

では、これらを実現するために採用したシステムについての説明をしていきます。

まず要望として挙がったのが、光彩やグラデーションを手柄にかけたいというものでした。
色付けは加算、乗算、アルファブレンドで元絵と合成したいというものです。
対象としてはSpineとLive2D、Sprite Rendererを使用していますが、それらが対象になります。
前提として、Spine Live2D Sprite Rendererはそれぞれ専用シェーダーが使われているため、別のシェーダーになります。

シェーダーは異なるものの、なるべく共通化しておきたいという気持ちもありました。
また、プロジェクトとして「グラフィックにこだわってキャラを魅力的に見せよう」という方針があったので、他の演出でも使える応用しやすい手法を選んだ方が良さそうという感覚もありました。

そこで今回採用したのは、シルエットのキャプチャーを加工して元絵の上から乗せるといった手法になります。
処理のイメージとしては、まず対象のレンダラーをキャプチャーし、それからシルエット化をします。
そしてそのシルエットに対しては、シェーダーによる加工をし、元絵に対して重ねるというようなことをしています。

この加工方法ですが、ブラー・光彩・グラデーションを組み合わせて使えるようにしています。
それでは作例を見ていきます。

影あり
影なし

影の作例になります。
竜胆の頭に影がかかって、キャラクターがしっかり影の中にいることが表現できていると思います。

これは竜胆のキャラクターをキャプチャ → グラデーション → 黒で色付けしたものを、元の絵に対してアルファブレンドで合成しています。

影あり
影なし

続いて複数体キャラを配置しているもですが、こちらはキャラクターそれぞれに対して、細かくグラデーションで範囲指定をすることで、キャラクターごとに影の位置が変わるように設定されています。

ライティング効果あり
ライティング効果なし

続いて光の表現、共鳴りの呪力の光を表現しているものになります。

手前のわら人形のところを見てください。
効果ありの方は光がわら人形に当たって、リッチな表現になっているかと思います。

これも同じようにわら人形をキャプチャ → グラデーション→ 青で色づけしたものを、今度は元絵に対して加算合成したものになります。

内側光彩効果あり
内側光彩効果なし

続いて背景の白が、手前の釘崎に対してかかっていくような、光が漏れてくるのを表現しているものになります。

これも同様に、釘崎のキャプチャから光彩を抜き出し、それを元絵に対して加算合成で載せています。

オーラ表現あり
オーラ表現なし

こちらはバトル中の強敵出現演出です。
キャラクターの外側に禍々しい光をまとうような表現になっています。

ボスキャラクターをキャプチャ → ブラー → ボスキャラクターの描画前に乗算合成しています。

キャラクターのキャプチャを行うので、パフォーマンスが気になるかもしれません。

負荷対策として、シルエットの画像のサイズを512×512に固定しており、少し小さめのサイズにしています。
また、描画カメラを1台に絞って、カメラの増加によるコストを抑制しています。
こちらはRender Featureを駆使して、カメラを増やさずにキャプチャーするテクニックを使っています。
このキャプチャの方法については、2Dキャラをリッチに見せる描画テクニックという別の発表で紹介していますので、今回は割愛させていただきます。

負荷対策をしているものの重めな処理であることに変わりはないので、バトル中など負荷が大きい画面では多くは使わないようにしています。

実際にあったこととして、開発中にバトル画面で表示するキャラ全8体のソフトシャドウを同じ仕組みで付けていましたが、これは負荷軽減のために仕組みを変更しています。

一通り説明が終わったので、陰影/光の表現についてまとめます。
シルエットを加工配置することで、様々な表現が可能になっており、特にアニメ風な表現と親和性が高いです。
パフォーマンスもストーリーパートで使用する分には問題ないです。
今後のタイトルでも活躍が期待できる表現手法だと思っています。

キャラライティングについて

『呪術廻戦 ファントムパレード』では高いクオリティのグラフィックで、キャラを魅力的に表現しようという指針があり、Live2Dに対する光の表現にもこだわっています。
当初は先ほど紹介した光の表現を使って、線形グラデーションと加算による光の付与を行っていたのですが、より一段とレベルアップさせるために、ノーマルマップを採用したライティングにチャレンジしました。

それでは、Live2D × ノーマルマップによるライティングシステムについて説明していきます。

ライティング表現あり
ライティング表現なし

効果ありなしで、かなりリッチさに違いが出てくるかなと思います。

実際に背景にライトが置いてあって、移動すると光の当たり方が変化します。

それでは、システムについて話をしていきます。

基本的には一般的な2Dライティングと変わりません。
3D空間上に光源を配置して、2Dの距離によって減衰する方式を採用しています。
ここではLive2D × ノーマルマップ特有の、特殊な対応が必要とされる部分について説明していきます。

まず一般的な話として、光が当たることでモノがどれくらい光って見えるかは、光の向きと物体の向きによって変わります。
光源の位置はプログラム上で把握できるので、Live2Dの絵が描画されるとき、各ピクセルに映るモノがワールド空間上でどの向きを向いているかが分かれば、当たる光の強さが計算できることになります。

ノーマルマップには、各ピクセルに描かれた絵の面の向き(法線) が書き込まれています。
図の右側の青いテクスチャがノーマルマップになりますが、このRGBが面の向きベクトルXYZです。
Live2Dはカメラに対して、正面を向いているため、Z成分が強くなってだいたい青になります。
顔の側面など、光の当たり方を変えて立体感を出したい場所に対してのみ、少し色を載せています。
接空間のノーマルマップになるため、テクスチャ右側への傾きはR成分が強く、左側への傾きはR成分が弱くなっています。

Live2Dの絵は一枚絵ではなくパーツアニメーションによって回転します。
ノーマルマップに書かれた法線の向きも、アニメーションに合わせて回転してあげる必要があります。

一般的には、アニメーションによる回転値を吸収してノーマルマップを活用するために、頂点に記録した接ベクトルを活用する手法がよく取られます。
しかし、Live2DのSDKには接ベクトルに相当するものがなかったため、接ベクトルにかわるものを自作する必要がありました。

そこで、『呪術廻戦 ファントムパレード』でとった手法は、2つの頂点の位置とUVから、テクスチャがどれだけ回転されているのかを推定し、それを法線にも適用する方法です。

パーツのメッシュごとに2つの頂点を適当に取り出して、位置の差分を、diff_pos とおいて、UVの差分をdiff_uvとおきます。

この時、回転角は

var cos = Vector2.Dot(diff_pos.normalized, diff_uv.normalized);
var sin = Vector3.Cross(diff_pos.normalized, diff_uv.normalized);

と表現でき、cos, sin を求めることができます。
そこからさらにフラグメントシェーダーで、ノーマルマップから取り出した法線を回転させてあげると、ピクセルごとにワールド空間における向き normal_ws を算出することができます。

const half3 normal = UnpackNormal(tex2D(_NormalMapTex, IN.texcoord.xy));
const half x = normal.x * cos - normal.y * sin.magnitude;
const half y = normal.x * sin.magnitude + normal.y * cos;
const half3 normal_ws = half3(x, y, -normal.z);

最後にワールド空間におけるライトの位置と、先ほど計算した向きから反射率を求めてあげることで、光の強さを計算でき、Live2D x NormalMapによるライティングが行うことができました。

それでは、キャラライティングのまとめをします。
Live2Dに対してライティングを適用して、キャラの魅力を大きく向上しました。
2つの頂点の位置とUVから法線の回転角を推定して、Live2Dへのライティングを実現しています。

レイヤー指定ポストエフェクト

続いてレイヤー指定ポストエフェクトというものを紹介します。

奥、通常、手前の3レイヤーに任意のオブジェクトをレイヤーとして分類して、奥または手前のレイヤーに対してポストエフェクトをかけられるようにしています。

まず手前レイヤーに対してブラーをかける例になります。
具体的には、手前に置いた呪力のパーティクルとわら人形のSpineに対してブラーをかけています。

では仕組みを見ていきます。
手前のレイヤーを別のレンダーテクスチャーに焼いて、アルファを含めてブラー処理をかけています(わら人形と呪力のところです)。
これをもともとの釘崎の絵に重ねてあげることで、この絵を作り出しています。

次は手前のレイヤーに対してのみ、ディレクショナルブラーをかける例です。

ちょっと分かりづらいかもしかもしれませんが、奥の背景の部分にブラーはかかっていません。
手前の虎杖と雲の呪霊にだけディレクショナルブラーがかかっています。

これもほぼ同じでブラーの対象としたい虎杖と雲を、別のレンダーテクスチャーに焼いてディレクショナルブラーをかけた上で元の絵と合成しています。

また、合成可能な手前レイヤーに少し制約があるので、それについて説明をさせてください。
背景に対して加算またはアルファブレンドする絵にのみ対応しています。
手前のレイヤーのレンダーテクスチャーは、加算とアルファブレンドに対応するため、事前乗算テクスチャー (RGBにあらかじめAを乗算した値が格納してあるテクスチャ) の形を作る必要があります。
その後、元絵との合成をBlend One OneMinusSrcAlpha で行うことで、加算とアルファブレンドの合成を実現させています。

なのでファンパレではストーリーパートで使用する全ての、シェーダーのブレンドモードを、Blend One OneMinusSrcAlphaにすることで対応しています。
例外的に乗算など一部の表現のためのシェーダーは、使用可能にしていますが手前レイヤーの合成を、使用する際には気をつけて使用するようにしてもらっています。

続いて奥レイヤーのポストエフェクトになります。

この例では奥のレイヤーにだけブラーをかけていて、竜胆と背景のみがぼけている状態です。
一方、手前にいる釘崎ははっきりと表示されているのがわかるかと思います。

作りとしてはさっきのものよりも少しシンプルで、奥のレイヤー、竜胆と背景のところをまず描画しまして、フレームバッファーにポストエフェクトをかけています。
その後通常レイヤーのオブジェクトとして、釘崎を上から描画してあげるというようなイメージになります。

それでは、実装について紹介します。
ファンパレではURPをベースとしたレンダーパイプラインを使っていて、Render Featureを使って処理を挿入することで、先ほどの処理フローを実現しています。

図のように通常の不透明描画処理の後で、奥レイヤーの描画とポストエフェクト、通常の半透明描画処理をしています。
そして、手前レイヤーを別のレンダーテクスチャーへ描画、手前レイヤーを別のレンダーテクスチャーへ描画。
ポストエフェクトをかけて、そのレンダーテクスチャーをフレームバッファーに合成する処理を行っています。

各レイヤーの描画を行う際には、レンダリングレイヤーマスクによるカリングを行った描画コマンドを発行しています。

あらかじめ手前レイヤーのオブジェクトと、通常レイヤーのオブジェクト、奥レイヤーのオブジェクトに、それぞれレンダリングレイヤーマスクを割り当てて、それぞれの描画コマンドで各レイヤーのみを描画するイメージです。

Unityの既存の描画順制御よりも優先して描画順が変更されてしまうため、緻密な描画順の管理が必要になります。
そのため、もしかするとソーティングレイヤーなどで、カリングを行った方が素直かもしれません。

また、パフォーマンスの観点から、レイヤー指定のポストエフェクトの設定がないときには、自動的に描画処理をまとめる工夫も行っています。

Live2Dの半透明描画に関するチューニング

最後のトピックとして、Live2Dの半透明描画に関するチューニングの事例をご紹介します。

Live2Dには、半透明で描画すると関節が重なってしまって汚れてしまうという課題があります。
これに対してよくある解決手法として、専用カメラを用意してキャラクターをレンダーテクスチャに描画 → レンダーテクスチャをFrameBufferに描画する際に、一律透明化させる手法があります。

ただ、これはシンプルに実装をすると、カメラが増える分CPUコストが増加したりなどレンダーテクスチャを増やす分、使用メモリが増えてしまうという問題があります。

『呪術廻戦 ファントムパレード』では特にバトル中にもLive2Dによる会話劇を行う演出があり、メモリ使用量の増加を許容することができませんでした。
また、ADVにおいて、従来タイトルよりもリッチなポストエフェクトを多く使っていたため、複数カメラを扱うことによるCPU負荷も課題になっていました。
そこで、本格的にこの課題に取り組んでいます。

それではまず、従来手法のフローを考えてみます。

はじめに、Live2D全体が映るようにレンダーテクスチャを用意します。
ファンパレでやっていたときには、キャラクター全体を映して描画する手法をしていたので、1800ピクセル×1500ピクセル×RGBで32ビット というサイズを扱っていました。

簡単に実装する場合を想定すれば、出てくるキャラクター×大体10MB、頑張って同時表示数分だけのレンダーテクスチャの確保にすれば、同時表示数×10MB程度で済みます。
とはいえ、30~50MBかかると思うと、かなり大きいです。

専用カメラを用意してレンダーテクスチャに描画することを考えると、Live2Dを撮影処理自体のコストもかかってきます。

処理の流れとしては、図の西宮と三輪が出ている場面では、西宮, 三輪 をそれぞれレンダーテクスチャに書き込み, それぞれ半透明でフレームバッファに描画して最終的な絵になるというような形になっています。

これをファンパレの描画フローでは図のように改善しています。

半透明フルスクリーンサイズのレンダーテクスチャを1枚だけ用意しておき、Live2Dを描画する直前にレンダーテクスチャに書き込むことでレンダーテクスチャを使い回そう、というフローです。

主なメリットとしては、何体同時表示してもレンダーテクスチャが1枚で済むことです。

実は、先ほど話にでていた手前レイヤーのポストエフェクトに使うフルスクリーンの透明バッファをそのまま使えるので、レンダーテクスチャのメモリ使用量は実質タダです。

さらに、描画用のカメラを増やさず、RenderFeatureで描画コマンドを発行することで、カメラ増加によるCPUコストも抑制しています。

デメリットとしては、実装コストが大きいことです。 Live2Dの描画順を把握して描画コマンドを手動発行する必要があるため、全オブジェクトの描画順のコントロールが必要になります。また、半透明の合成が必要なので、Live2DのシェーダーもBlend One OneMinusSrcAlphaでブレンドするという制約もつきます。

ただ、実装さえしてしまえば、頭を悩ませてきたLive2Dメモリ使用量多すぎ問題とCPU使いすぎ問題から一気に解放されるので、やってよかったなと思っています。

まとめ

呪術廻戦ファントムパレードで広く活用しているグラフィックシステムについて紹介しました。

  • 陰影光の表現
  • キャラライティング
  • レイヤー指定のポストエフェクト
  • Live2Dの半透明描画に関するチューニング

どの手法も使い勝手が良くクオリティーが高いので、今後のタイトルでも活用していくと思います。

皆様の開発するプロジェクトにとっても良いノウハウとなっていたら幸いです。

©芥見下々/集英社・呪術廻戦製作委員会 ©Sumzap, Inc./TOHO CO., LTD.

二宮 章太

株式会社サムザップ クライアントエンジニア