
clusterのワールドで光を使った演出についてのノウハウ
メリークリスマス🎅 Cluster Creater #2 Advent Calender の最終日、25日目を担当いたします、こはだと申します!
clusterやVRChatなどのVRSNSでワールドをつくったり、イベントをオーガナイズしたり、DJやVJをしたりします😉

このたび、至日レイさんから背中PUSHを受けて、はじめてAdvent Calenderを書かせていただきます⭐⭐⭐
VRSNSでのこれまでを述べると、2022年の6月にclusterをスマホで始めて、その後、ゲーミングパソコンを買って、Unityの使いかたを覚えて、8月には初めてのワールドを作り、2022年10月あたりからVRChatでも遊ぶようになり、いまでは10か所の音イベ用ワールドを運用しています。
今回は、これまでワールドを作っていくなかで工夫してきた、音イベ用ワールドで欠かせない光の表現についてお話したいと思います。個人的なノウハウなので、あくまで一例として見ていただければ幸いです🙇♀️
内容的にはUnityの基本操作を知っている初心者〜中級者くらいの範囲を想定しています。Unityの使用バージョンは2021.3.4 です。
光の演出いろいろ
光の演出というと、何を思い浮かべるでしょうか?🤔
視覚的に影響しうるものを挙げてみると、
Skybox
Lightコンポーネント
リアルタイムなもの or ベイクするもの
Directional Light / Point Light / etc…
ビームライト風オブジェクト、ボリューメトリックライトオブジェクト
Light Probe
Reflection Probe
Emission
オブジェクトに対して
パーティクルに対して
Bloom(Post-Processing)
などがあるのではないかと思います(影の表現に関しても触れるべきかもしれませんが、割愛します!)。
これらの要素や、その組み合わせをもとに、いままで実装した事例をご紹介いたします。
昼と夜を切り替える
Skyboxを擬似的に本番上で切り替えるには
私のワールドでは、時間の経過によって昼🌞や夜🌒の切り替えを多用しています。その際に空の表現に用いられるSkyboxですが、Animationによる制御でマテリアルを切り替えることができません。
その時に活用するのがこちらのアセットです。
このオブジェクトにSkyboxのマテリアルを割り当てると、オブジェクトの内側が継ぎ目なくSkyboxのような景観にできるというものです。ただし、使用にあたっては、いくつか注意しなければいけないことがあります。
clusterでは1000m以上先にあるオブジェクトはCullされてしまう … オブジェクトの端から端まで1000mを超えると空が欠けた見た目になるので、その範囲内での大きさにとどめる必要があります。
Skyboxのマテリアルはデータサイズが大きい … 調子にのってたくさんの種類をプロジェクトに入れるとダウンロードサイズが大きくなります💦。演出上、必要なものに絞るのと、多く使い分けるなら、1024pxサイズにするとよいでしょう。
そうして複数設定した「オブジェクトでのSkybox」をトリガーとAnimationで切り替えます。
切り替えにあたっては、Animationで時間をかけてゆっくり目に入れ替えていくとキレイです。

オブジェクトでのSkyboxと合わせて変えると効果的なもの
Skyboxに合わせて、以下の要素も同じタイミングで変更すると効果的です。
Directional Light … Skyboxに合った明るさ、色になじませます。また、意外と重要なのが「向き」です。Skyboxの景色に合わせてTransformコンポーネントのRotationのXとYを調整すると影の角度が変わって雰囲気がよりマッチします。
Post-ProcessingのBloomの値 … 暗い中の光はすこしにじんで見えると雰囲気が出るので、明るいSkyboxから暗く変化させるときには、Bloomをオン&最初はIntensityを0からスタートして、徐々に値を上げていくと効果的です。Bloomについては後述します。
Reflection Probe … 実はReflection Probeの「Cubemap」はAnimationから操作できる対象です。通常だと大元のSkyboxが映り込むようになっているので、そうしないようにTypeをCustomにして、好きなSkyboxマテリアルを指定することで、映り込み内容を変えることができます。(場所ごとにReflection Probeを設定していると、それぞれごとマテリアルの分岐を用意する力業になります)


暗くすると、アバターまで真っ黒になるんだが?
はい、そうですね。たとえば、上の写真の右側くらい暗くしようとすると(Directional LightのIntensityは0です)、アバターもこの通り真っ黒になります。

特にステージに上がっている演者さんまで真っ黒になると映えません💦。
そういうときは、リアルタイムのPointライトをステージ前方に置いて、Lightインスペクタ内の「Culling Mask」にて、ライトを当てるレイヤーを次のものに限定しておきます。Accesory / AccesoryPreview / FIRSTPERSON_ONLY_LAYER / THIRDPERSON_ONLY_LAYER / OtherAvatar / Performer / Audience

リアルタイムライトは負荷対策で考えるとネガティブですが、Directional Lightを消していることと、上記のとおり、レイヤーを限定してアバターにのみ当てるということで配慮しています。もちろん、必要なときにだけAnimationでPoint LightをActiveにしています。
あと、大事なのは、リアルタイムのPoint Lightは、アバターの前方から当てることです(なので「ステージ前方に配置」としています)。皆さんがよく使われるVRoidアバターの場合、裏側から当てると、ホラーみがたっぷりの怖い感じになります。
明るくしたり、暗くしたりするのに、ライトベイクしてもいいの?
ライトベイクは、リアルタイムの描画計算負荷をなくす一方で、ある光の状態でライトマップが作られて、テクスチャのようにオブジェクトに貼られるため、ワールドの光の量を変えるとオブジェクトが浮いて見えるのではないか、と心配されるかと思います。
ですが、ある設定をしておけば、ライトベイクをしたあとで、リアルタイムライトの明るさを変えても、ちゃんと馴染ませる方法があります。
以下、Bakeryでライトベイクする前提で設定例を書きます。
BakeryのRender modeを「ShadowMask」し、Occlusion Probeにチェックを入れる。
ワールドの光源となるDirectional LightのModeを「Mix」にする。
同じDirectional Lightのオブジェクトに、Bakery Directinal Lightコンポーネントを追加し、Bake contributionをShadowmask and Indirectにする。
Light Probeをベイク範囲に設置しておく。
という手順でベイクすると、ライトマップはShadowmaskとして、影が乗算で描写されるようになり、明るい時は影が見え、暗い時は影が沈むような見た目になります。
光るオブジェクトを作る
これは基本的なテクニックなので、皆さんが普段から使われていると思いますが、ノウハウ集として幅広く触れるため、書いておきます。
Emissionをオブジェクトに設定する
オブジェクトを光らせるためにはシェーダーのEmmision(=発光)を設定します。シェーダーによって使えたり使えなかったりしますが、おなじみのStandardシェーダーだとここになります。

EmissionのカラーピッカーはHDRカラーピッカーであることにも注目です。HRDと付くものだとIntensityという設定項目があって、ここで発光の強さを調整します。これを0よりも大きい値に設定すると、環境の光が暗くても、明るいままで見えるようになります。(「Emissionにチェックを入れているのに光らないやーん!」って方は、Intensityの値を見てみてくださいね)


上の写真のように、テクスチャの一部にだけEmissionを効かせることもできます。ここでは、布地の模様の一部が、ブラックライトを照らしたときの発光塗料のように、うっすらと光るような表現を狙いました。効き具合を示すテクスチャをグレースケールで作って、EmissionのColorの脇の画像設定のところに割り当てると機能します。(白色に近いほど反映される)

Standardシェーダー以外にも、Emissionを設定できるシェーダーはあります。たとえば、Particle用シェーダーだとここです🌟。


パーティクル自体が発光すると幻想的でとてもきれいです!
ぜひおためしをーーー!
EmissionとBloomの設定は両輪のようなもの
暗い空間でEmissionだけ効かせてオブジェクトを置いても、景色になじみません。明るい色にはなっているけど、場に不釣り合いで、光っているようには見えない、みたいな。
そこで、Post-ProcessingからBloomというものを利かせます。


オブジェクトのEmissionのIntensityの値と、Post-ProcessingのBloomのIntensityの値は、合わせた結果で見た目が得られるため、両方の数字に気を配る必要があります。ただ、すんごい明るすぎたときとか、オブジェクトごとに発光の度合いをすべて調整するのは手間なので、設定の自由度を高めるために、Post-ProcessingのBloomのIntensityは抑え目にしておくのがおすすめです(0.xくらいの値)。
Bloomの設定項目も、いろいろあります。

この中で最重要なのが、さっき触れたIntensity(強さ)と、Threshold(スレッショウルド=閾値)です。
Thresholdは設定している数字よりもEmissionのIntensityが小さなオブジェクトはBloomを効かせない、という意味で、これを高くすると、オブジェクトのEmissionのIntensityが強くても、めったなことでは光らなくなります。いろんなオブジェクトがEmissionが設定されまくりだけど、光らせたいところとそうでないところのメリハリを出したいときに設定します。
あと使う可能性があるものといったらDiffusionでしょうか。ここの値を大きくすると、光のにじみの幅が大きくなって、よりまぶしい雰囲気にできます。


いままで使ったことがなかったですけど、Dirtnessというのも面白そうです。曇ったゴーグルをかぶっているような雰囲気が出せます。何かで使ってみたい。。。テクスチャサンプルはUnityのプロジェクトを作ったときに付いているアセットの中に入っています。

ビームライトでステージを飾る
さて、私的にはここが本稿のメインとなります。
いつも実際のライブステージに備え付けられているビームライトみたいなのをVRの世界でも再現したいと思っていて、照明の知識はぜんぜんないのですが、見よう見まねでやってきました。

音イベでは、このライトを操作して、音に合わせた演出の一部にするのですが、どういう仕組みにしているのか、要素分解しつつ、それぞれをどう扱うかについて振り返ってみたいと思います。
ビームライトを組み立てるときの要素
私がビームライトをワールドに実装するときは、以下の要素に分けて設置したり、操作用のトリガーとアニメーションを組みます。
ライトのレイアウト
基本のライトレイアウト
ライト設置土台ごと移動させる場合のオプションレイアウト
ライトの可動部分
横方向の回転軸
縦方向の回転軸
ライトの動き
静止ポジション(複数バリエーション)
ゆっくりした動き(複数バリエーション)
早い動き(複数バリエーション)
ライトの明滅
全体(ゆっくり / ふつう / はやく)
交互(ゆっくり / ふつう / はやく)
ランダム(ゆっくり / ふつう / はやく)
ライトの強さ
暗い
中間
明るい
ライトの太さ
狭い(レーザーみたい)
中間
広い(やってみるとわかりますが、すごくえもい)
ライトの色
ライトの天井・壁面・床への反射を別手段で作る
ライトの配置レイアウトを決める
最初に、どのようにビームライトを設置するかを考えます。私はいつも、YouTubeとかで好きなアーチストさんのライブ映像を見たり、VRSNSでの音イベを見たりしてて、これと似たようなのをつくりたいー!っていうところからスタートします。
ただし、EDMのステージみたいに、ライトの数を増やしすぎると、見栄えは荘厳になりますが、一方で、ユーザーの表示負荷と、アニメーションを組む際の作業が大変なので、慣れるまでは無理はしないのがおすすめです。
これはあるワールドでの組み方の例です。

ステージを正面から捉えたときに、上部に横一列、下部に左右2組、やや後方に左右一列ずつ、上部に奥行き持たせて左右一列ずつ、といった組み方です。あとでAnimationをさせるときに、タイムライン上でのオブジェクト名の判別ができることを考えて、それぞれのかたまりごとに親オブジェクトでくくり、上下左右のどれなのかが認知できるわかりやすい名前を付けておきます。

次に、それぞれのかたまりの中で、ライトの個数と配置の間隔を決めます。ライトの個数は、真ん中で割って左右対称に動きを作るときは偶数個、中央のライトに何かさせたいときは奇数個にします。
配置は、どのかたまりの中でも同じ間隔で並べると仕上がりがきれいになります。等間隔で並べる方法は、こちらの記事にあるとおり、オブジェクトを複数選択してから、TransformのPositionの欄に"L(最小値,最大値)"と書くことによって、この範囲で等間隔に配置されるので、活用してたりします。
次に、配置したライトの名前の付け方です。Animationのウィンドウでは、上から下にアルファベット順でオブジェクトが並ぶため、XXX01, XXX02, XXX03みたいに順番に名前を付けます(XXX1, XXX2, XXX3だと、10個以上扱う時に支障がでます)。ここは地味ですけど大事なところです!

上の写真のとおり、動かしたい子オブジェクトそれぞれに対して判別名称をつけるのは大変なので、オブジェクトのパス全体の文字列の若さで並んだときに、それがシーンビュー上での左から右とか、上から下の並び通りになっている必要があって、ここをてきとうにしておくとAnimationの動きをつけるときに大変苦労します!
そんな感じで、ライトの配置レイアウトを決めて、グループ化して、一つ一つのライトのオブジェクトの命名規則を整えます。
ライトの可動部分を知る
次に、ライト一つ一つをどう動かすか、についてです。
noribenさんのこのライトを例に作りについて見てみます。

一つのライトのオブジェクトを分解すると、
Base … 台座の部分。動かさない。
Arm … 横方向の回転軸。Y軸でしかRotationさせない。
Light … 縦方向の回転軸。Z軸でしかRotationさせない。
LightShaht … 光本体の部分。動かさない。
このように、横の回転方向と縦の回転方向の組み合わせで動きを作ります。横回転の仕事をするオブジェクトに縦の仕事はさせないですし、その逆もしかり、です。

ライトの静止ポジションを決めて、アニメーションをつける
次はライトのアニメーションをつけていきます。
すごく伝え方がむずかしくて、私のイメージを言葉にむりやりした感じなのでわかりにくかったらごめんなさいmm。
最初に決めるのは「往復」のパターンです。
動きはじめのポジション
中間のポジション
動き終わりのポジション
ループできる動きにするために、1.と3.は同じポジションにします。
1.と2.、2.と3.の間には、観客から見て、視覚的な印象が生まれることを重視します。例えば、
光線で観客をスライスするように動かす … 光が迫ってくるように見える
観客の位置から見て意味のある角度変化にする … ちゃんと動いて見える
などです。
もう一つ意識していることは「左右対称」です。
中央で割って対称の動きをつける
一つずつ交互に対称の動きをつける
いくつかのまとまりで分けて、まとまり単位で対称の動きにする
など、動かし方のルールを決めると、動きがまとまってきれいにみえるのと、キュー数の多いAnimationも作りやすくなります。
と、いろいろ難しく書きましたが、動かす対称は、Arm部分のY回転軸と、Light部分のZ回転軸だけで、それぞれをどのくらいの幅の角度を動かすか、どんなタイミング・順番で、どのくらいのスピードで往復運動を施すか、ということになります。

上の図は動き方のアイディアです。
作業が破綻しないように、角度の付け方はなにかの規則性を付けています。
末広がる感じにするなら、中央から端にいくにつれて、5度、10度、15度、と角度をつけていったり、対称の反対側に関してはすでに入力した角度の数字の頭にマイナスをつけてあげたり、そんな感じです。

動かし方は、演出意図によりさまざまではありますが、私の好みでは、
狭い範囲をゆっくり往復する動きのループ(音楽のイントロやブレイクをイメージ)
早い動きと遅い動きを混ぜた、長い尺のループ
一個ずつ交互に左右対称の動きをつくり、交互に点滅させるもの(ビルドアップやドロップをイメージ)
をAnimationのパターンとして作ります。
Animationはそれぞれのグループごとに、同じ数だけAnimationのパターンを作り、それらを組み合わせて、組み合わせの単位でワールド内のトリガーで動き方を指定できるようにします。あまり細かくグループごとに動きを設定する作りだと、ライトを担当する人が操作に手が回らなくなるため、単純化しています。
また、Animation Controller上での遷移で多少のDurationを入れておくと、アニメーションのトリガーの変更の操作自体でもライトをにゅっと動かせる感じにできますので、そこも意識しています。


また、ライトの動き、明滅、強さ、太さ、色など、すべての要素は、別々のAnimationをブレンドできるように、すべておなじAnimation Controller内に収めて、Layerで分けています。
違う要素(例えばライトの向きと色とか)を同じAnimationファイルの中で作ると、作るときに混乱しますし、異なる要素を違うタイミングで重ねられなくなるため、そうしています。

光の点滅、強さ、幅などの設定
点滅は、LightShaftのActiveの切り替えで制御して、それ以外は、このライトのシェーダー部分の数値操作で処理します。

Color > Colorがライトの色
Color > Intensityが光の強さ
Size > Widthが光の太さ
Size > Lengthが光の長さ
で、このあたりをAnimationのIntトリガーで切り替えられるようにしています。
点滅をシェーダーのIntensityで制御しようとすると、光の強さの表現と同じパラメーターでバッティングしてしまうので、点滅はLightShaftのActiveのON/OFFで制御しています。
点滅は視覚の印象づくりにはかなり有効で、全体を点滅させるのはもちろん、隣同士を入れ替わりで交互に点滅させたり、グループごとに点滅させたりで、光線の反復運動の集合体がさらにトリッキーでランダムに動いたり光ったりする雰囲気に見えるようになりますので、点滅のアニメーションパターン作りはこだわってもいいとおもいます!

ビームライトは実はライトではない
noribenさん、白百合めしべさんが提供されているビームライトは、シェーダーで光っているように見えるもので、周りを照らすものではありません。
なので、それを補うために、リアルタイムのLightオブジェクトを置いたりします。

こんな風に、Point Lightを真ん中あたりにおいて、ビームライトの色と同じ色とかで同じ空間を照らすようにします。リアルタイムライトなので負荷は重くなりますので、必要に応じてCulling Maskの設定項目から照らすLayerを指定して、描画計算を少なくするのもいいと思います。(ただし、床や壁など、ColliderをつけたオブジェクトをDefault以外にすると、別途Default LayerにColliderをつけなくてはいけなくなるので、ご留意ください)


最後に
今回は細かいところ多めにワールド制作のテクニックについて語らせていただきました。
まだ勉強中の身なので、これからもいろんなやり方を見つけて、はっとするような視覚的体験を作りたいと思ってます。あとはシェーダーを勉強してオリジナルの軽量なビームライトをつくってみたいぃぃ。。。
もし、この記事が誰かの役に立って、ワールドの表現が豊かになったら、VRの世界がより楽しくなりますし、ベテランや本職の方々が「もっといいやり方があるよ」ってご指摘いただいたらさらにうれしいと思ってます!
長文、お読みいただきましてありがとうございました😊
いいなと思ったら応援しよう!
