はじめに
以前も『呪術廻戦 ファントムパレード(以下、ファンパレ)』の技術記事を書かせていただきました、サムザップ、クライアントエンジニアの二宮です。
今回は、ファンパレでも最も力を入れて制作した演出の一つ、領域展開についてです。
領域展開の演出をどう考えて、どういう仕組みで実装したのか、書いていこうと思います。
領域展開は「呪術廻戦」特有の話ではあるので、他の開発にて役立つ話かどうかはわかりませんが、興味があればぜひ読んでみてください。
領域の表現
まず、領域をどう表現したのかについて話していきます。
領域はひとつずつシンプルにUnityのSceneで表現しています。
Sceneの中には、ポストエフェクトや背景へのライト、パーティクルも存在します。また背景固有の設定として、キャラクター・敵へのライトやFogなどの表現も採用しています。
各領域のための専用Sceneを作っておき、領域展開演出時には表示している背景の切り替えを行います。
キャラクターの領域展開
キャラクターは必殺スキルによって領域展開を行うため、必ず専用のカットイン動画が流れます。
カットイン動画の中ではキャラクターを映して演出を行うとともに、動画の一部を透過させることで現在設定されている背景を映すようにしています。動画の動きに合わせたカメラワークで背景を映すことで違和感なく、ダイナミックな演出に仕上げています。
五条 悟の領域展開
伏黒 恵の領域展開
真人の領域展開
また、動画が終わると実際のバトル背景も領域になっている必要があるので、動画演出中のどこかでアルファ抜きのないフレームを作って、その裏で背景の切り替えを行っています。
背景Sceneはそれなりのサイズになるのですが、背景の変更は一瞬で行う必要があります。
スキル演出中に背景を読み込むのを待つのも違うし、画面が少し固まってしまうのもなるべく避けたいです。
そこで、バトル中に発動しうるキャラクター・敵の領域は、全てバトル開始時に読み込んでしまう工夫もしています。
こうすることで、背景の切り替えは描画On/Offを切り替えるだけで実現できるようになるため、スムーズに切り替えることができます。
敵の領域展開
敵については、領域展開を行うときにもカットインは出ません。
そのため、動画に頼らない領域展開演出を作る必要がありました。
それでは作ったシステムについて紹介していきます。
2つの背景の合成方法
まず、通常のバトル背景から設定された領域をじわじわ切り替えるために、2つの背景を写す2つのカメラを用意して、2枚の絵を合成する方法を採用しています。
画面にはよく見ると、通常のバトル背景が写っている箇所、領域が写っている箇所、両方がブレンドされている箇所 の3パターンが存在することがわかります。
「画面上のどのピクセルにどの背景の絵を合成するのか」については次のエリア指定マスクテクスチャを使っています。
白が領域のエリア、黒が通常背景のエリア、グレーがブレンディングされている境界のエリアになります。
エリア指定マスクテクスチャの作り方
当初エリア指定マスクテクスチャを手動で作って演出の制作チェックをしてみたところ、「カメラのカットを切り替えると必要なエリア指定マスクテクスチャがガラッと変わってしまって大変」という話があり、カメラのカットを自由に切り替えられるエリア指定の仕組みを作ることになりました。
そこで、3D空間上で領域に切り替わっているエリアを定められるようにして、そのフレームのカメラからの見え方をエリア指定マスクテクスチャに書き込むことを考えました。
この3D空間ベースでの領域の指定方法もかなり悩んだのですが、漫画原作やアニメを見返してみると、領域は発動者付近または発動者の後方から広がっていくと、自身の領域が広がっていくようにみえることに気づき、エリア指定の方法もこの考え方をベースに検討しました。
仕組みとしては、見えない球をどこかに置いて内側を展開する領域とする方法です。球の半径を広げていくと領域が広がることになります。カットを変えても球の位置や大きさが変わるわけではないので、きちんと領域になっている場所の整合性がとれるのがポイントです。
- フィールドのどこかの座標に見えない球を配置
- 領域の背景の地面部分をエリア指定テクスチャに書き込む
- 球の内側は発動した領域のエリア
- 球の外側は通常背景のエリア
- 球面付近はブレンドエリア
- 領域展開演出の中で球の半径を広げていく
// 背景オブジェクトに付与するエリア指定マスクテクスチャ用のshader // 描画しようとしているピクセルのworldSpace上のポジション から 球の中心座標 (BASE_POSITION) を引いて, 球の中心から描画しようとしているポイントの距離(distance)を求める const float3 diff_vector = input.vertexWS - BASE_POSITION; const float distance = sqrt(dot(diff_vector, diff_vector)); // ブレンディングを行う距離 (EDGE) と, 内側の領域と判定するための距離 (DISTANCE) を使って, 球の内側を白 (1), 球の表面付近をグレー (0 ~ 1), 球の外側を黒 (0) として色の値を取得する half color = saturate((DISTANCE - distance) / (EDGE + 0.001)); return half4(color, color, color, 1);
領域展開演出のシステムフロー
最後に、領域展開の演出のフローについてまとめておきます。
- 領域背景をRenderTextureに描画
- 通常背景をフレームバッファに描画
- 領域背景と球の設定から、エリア指定マスクテクスチャを作成
- エリア指定マスクテクスチャに基づき、フレームバッファに領域背景のRenderTextureを合成
カメラを増やすと負荷が大きく増えるので、マスクテクスチャの作成やRenderTextureの合成をRenderFeatureで実現することでカメラ1台増に抑える工夫をしています。
これで、敵であっても領域を広げる表現を実現することができました。
領域周りの小話
せっかくの機会なので、少し細かい領域の仕様への対応方法についても触れておきます。
領域の上書き
ファンパレにはすでに展開されている領域に対して、領域を上書き展開することができる仕様があります。その場合、演出としては通常背景 - 領域 ではなく、領域1 - 領域2で同じことをします。
- 新しく展開する領域をRenderTextureに描画
- 消えゆく領域 をフレームバッファに描画
- 新しく展開する領域 と球の設定から、エリア指定マスクテクスチャを作成
- エリア指定マスクテクスチャに基づき、フレームバッファに 新しく展開する領域 のRenderTextureを合成
描画先のオブジェクトの決定はgameObject.layerによるカリング設定で行っています。そのため、対象の背景を切り替えるのは該当Sceneのオブジェクトのレイヤーを切り替えるだけで済むようになっています。
伏黒 恵 - 嵌合暗翳庭
伏黒 恵の領域展開、嵌合暗翳庭(かんごうあんえいてい)は、通常のバトル背景に対して領域が重なります。
そのため、嵌合暗翳庭に関しては嵌合暗翳庭背景Scene+通常バトル背景 で一つの領域背景とみなして演出を作っています。
- 嵌合暗翳庭+通常背景をRenderTextureに描画
- 通常背景をフレームバッファに描画
- 嵌合暗翳庭+通常背景と球の設定から、エリア指定マスクテクスチャを作成
- エリア指定マスクテクスチャに基づき、フレームバッファに嵌合暗翳庭+通常背景のRenderTextureを合成
レイヤーの割り当ては、例えば通常背景をレイヤーA、嵌合暗翳庭をレイヤーBとして与えた時に、カメラ側のカリング設定をA+BのカメラとAのカメラといったような構造にすれば同じ仕組みで対応することができました。
おわりに
領域展開演出の仕組みについて、動画を使った自由度の高い演出方法と、動画なしで実現する領域の演出方法についてお話ししました。
お察しの通り、敵のための領域展開の方が頭を悩ませました。「呪術廻戦」という作品において領域展開表現のできは非常に大事。動画という何でも表現できるツールに頼れない分、システムによって変な制約をつけてしまわないよう、クリエイターがアイデアを実現しやすく最も使いやすいツールを心がけて仕組みを構築しました。
皆様にとって、ファンパレの領域展開演出が少しでも魅力的に映っていれば幸いです。
最後まで読んでいただきありがとうございました。今後とも『呪術廻戦 ファントムパレード』をよろしくお願いいたします。
©芥見下々/集英社・呪術廻戦製作委員会 ©Sumzap, Inc./TOHO CO., LTD.