22日目です。今までに話さなかった残りの話について書きます。
Lighting
Amebientは全空間が繋がっているようになってます。「世界全体で1つ」にしたいとか、相互移動がしやすいようにとか、色々あるとは思います。
代わりに各空間は機能によって分断されていて、それは自然に生まれた違いです。だから全体のLightingはすごい素直です。
Directional Lightが3灯。2つがBake用、1つがRealtime用。全部同じ向きです。
Bake用のライトは「太陽の代わり」と「環境光の代わり」として使っています。太陽だけだとUnderFloor (バケツがあるところ) が暗すぎたので、NoShadowで暗めのライトを置いています。
Bake設定はCapと私で弄っていた記憶があります。AO入れたのとIndirectIntensity上げたのは私だと思う。
Realtime太陽のレイヤーはEverythingなので建物の影は全て二重に掛かっています。物理的には正しくないですけど案外素直に受け入れられた気がするんですよね。特にRealtime影の境界がBaked影の境界と混ざっているのでリアルタイム影のアーティファクト感とかが減っているような気がします。
VRChatのワールドでは影が消されていることが多いと思います。それは描画負荷的には仕方ないことであり当然とさえ思いますが、Amebientには影があって欲しかった。ここはToonyでもPhysicallyBasedでもない世界だと思っていて (VRChatのことですね)、そしてここは影が存在する世界な気がしていた。
これによって負荷が上がっている部分はあると思いますが (Batchesの1/3)、得られた恩恵は大きいと考えています。これの2枚目とか。
**作戦会議**をしました。 pic.twitter.com/zzEUpQnkfi
— Cap.☀ (@CaptainAyakashi) June 24, 2020
加えてスロープのあるところにはいくつかライトが置いてあります。
これはEditorOnlyのEmissiveな球です。極端ではありますが、このスロープ部屋の穏やかな雰囲気 (特に一番最初に訪れたときの暖かさ) を直接的に与えることが出来ています。
配置は多少考えてます。「SubFloorからは一切見えない (対比的な冷たさを出したかった)」というのと「各階で見かけが異なるようにしたい」という目的で、UnderFloorからは小さく一灯だけ見える・MainFloorからは階段の上下に見える・屋上からは「屋上を示すランプ」のように梁の上に置いてある、っていう配置にしています。たしか。
あとちょっとAreaLightがあります。
Capが置いてくれました。これは大正解。元々外からの光はほぼ入ってなくて暗かったんですけど、このライト群によって「外の明るさ」を認識できるようになった。とても良い。
あとReflectionProbeは各階と各スロープ室、あと上空と海底 (0,-100,0
)にあります。海底のReflectionProbeにはSkyboxだけ映っていて、海や泡のMeshRendererにはAnchorOverrideでその場所を指定してます。それが正しいと思ったので。
PostProcess
**本当におすすめしません。**こんな最悪パラメータでよくあんなに安定した色味が出てますよね…。全パラメータを同時に最適化してたので正直ちゃんとした説明は出来ません。
まず湿度を出したかった。のでBloomをThreshold 0・Diffusion 10で入れました。全力です。そしてアバターを光らせたくは無かったのでClamp 0.75になってます。Amebientにある物体はそんなに明るくないんですがアバターに何が入ってるかはわかったものではないですから… (確か私のUnlit髪飾りを見て調節してた気がします)。
そしてIntensity 4。これは後に全体の光量を落とすことで相対的にブルーム量を増やす為です。
Bloom自体はadditiveで掛かってしまうわけですが、Post-exposureで光量を落とせばblending的に掛けることができます。
Bloomを抜いて光量を概ね合わせるとだいぶぱきっとした感じになりますね。
Bloomのぼかし効果によって空の描画のアーティファクト軽減になっている部分もそこそこあります。
そしてColorGrading…。Amebientは暗い場所とそこそこ明るい場所、どちらもあるのでうまく両立させる必要がありました。Tonemappingは「マジで暗い場所は暗いまま」「中域はちょっと持ち上げる」「明るい部分は少しだけ抑える」感じになってます。
Amebientの空間は全体的にコントラストが低い (Bloomのせいもある) ので、その領域をいい感じに見やすくする必要がありました。ぐっと引き伸ばしているイメージです。
本当にこうなってるのかな。感覚で調節してるのでよくわかりません。色んな場所を見ながら延々とパラメータを弄っていました。
あとTemperature -5はまぁ温度感の調節。Constrast 20はちょっとだけ持ち上げるため (この状況下だとあんまり効果無い)。
TrackballsのGainはあんまり意味がない (Post-exposure 0の状態にする為かしら)。Liftは、暗所でのBloomを持ち上げる為です。上げすぎると暗所が暗所じゃなくなってしまうわけですけど、少しだけ入れることで暗所のコントラストを向上させることが出来ました。
— phi16 (@phi16_) December 8, 2020
そういえばBloomが全体に広がっているおかげで逆AutoExposureみたいになっているのも面白いですね。これが「空間の違い」を引き立てるのに寄与している部分はあるかも。
ちなみにVignettingとChromaticAberrationは自作のシェーダで動いてます。元々はPPSでやってました。
Hierarchy
なんとなく公開。
これでもめちゃくちゃ階層化してるタイプらしい (これが普通だと思っていた…)。トップレベルにあるのは5つで、World (全体の設定とか世界に関係ないものとか)、Mecha (ギミック系)、Machina (機械類)、Environment (ギミックっぽくないもの)、あとMisc (テスト的に置いたもの) として大きく分類しています。
階層化する際には原則としてTransformがデフォルトのGameObjectに入れるようにしていて、本当に概念的階層としての役割だけを持たせています。MachinaのMonitorたちには位置が入ったりはしてます。
大昔は階層化しすぎると再計算とかあるんかなと思っていた時期もありましたけど、なんかそんな様子は一切無いし、何より整理による利益がめちゃくちゃでかいので… ぱしぱし作っちゃっていいんだなという気持ちです。
特に「何かを追加しても見かけ上のHierarchyに変化が無い」のは大きいですね。いつも「Hierarchyパネルの真ん中らへんにシェーダ製エフェクトが並んでる」状態で作業をしていました。安心感。
階層化しようと思えばもっと出来るとは思いますけど、意味のない階層化はすべきではないという点で例えば「海用のシェーダ物体」とかをまとめたりはしないようにしています。位置的関連性は本質的にはどうでもいいし、シェーダ物体群として高々10個が横に並んでいる方がマシです。個人の感想です。
逆に「同じ物体」はできるだけまとめるようにはしていますね。それはそういう関連性がちゃんとあるからです。
あとおまけに… ちょっと違う話ですけど、prefabの運用について、色々あったような気がします。
一箇所でしか使われないモデルとかをprefab化しても本来は特に意味がないんですけど、複数人開発の場合には「変更の箇所が局所的になる」という大きなメリットがあります。ただ、「バルブを動くようにする」ような変更だと元メッシュの変更によってギミックが破壊されることがあって (あった) ちょっと悩んでいた覚えがあります。まぁそんなに変更回数は多くなかったので運用でカバーになってる様子ではあります。
あとPrefab Variantがめちゃくちゃ便利だった。要はオブジェクトの継承みたいな気持ちですね。缶も鍋もメッシュが違うだけで内部ギミックは同じなのでPercussionというprefabを作ってそのメッシュ差し替えとして作られています。加えて同じ物体をいっぱい置いたりもするのでそれごとprefabにするようにしました。正しい。
最近Amebientが壊れたのを治す時にPercussion prefabだけ弄って全てが治ったりもして良かった。まぁ正しく表現すると正しく振る舞うという話ですね。
開発についての話はこんな感じですかね…。ちなみにGitHubにリポジトリ置いてえいえいってやってました。
20200901
"Amebient"が30000visitsに到達しました🙌みなさんありがとうございます!
— Cap.🌥️ (@CaptainAyakashi) September 4, 2020
たくさんの方に訪れて頂けて、メンバー共々とてもうれしいです。
ところで、ここに偶然Amebientで遊んだときの記録映像があったので、記念に公開しました。https://t.co/Ss4svddgpq
この動画、フェードと最後のMaking部分以外は編集が一切入ってません。VRChatのいつものカメラです。
ちなみに動画編集は私がffmpegでやりました。
# making cut (adjust for resolution, frame rate, volume)
ffmpeg -y -ss 6.5 -i making_raw.mp4 -vf "scale=1920:-1, fps=fps=hh0" -af volume=-5dB -t 81 making_cut.mp4
# add Making text
ffmpeg -y -i making_cut.mp4 -i making_name.png -i Amebient_thema.wav -filter_complex "[0:v][1:v] overlay=W-w-100:40, fade=type=in:duration=2,fade=type=out:duration=1:start_time=80 [v]; [2:a] volume=-10dB [ba]; [0:a][ba] amix=inputs=2:duration=longest [a]" -map "[v]" -map "[a]" making_named.mp4
# fade
ffmpeg -y -ss 10 -i main_raw.mp4 -filter_complex "[0:v] fade=type=in:duration=0.5,fade=type=out:duration=1:start_time=753.8 [v]; [0:a] afade=type=in:duration=1,afade=type=out:duration=1:start_time=754 [a]" -map "[v]" -map "[a]" -t 756 main_fade.mp4
# concat
ffmpeg -y -i main_fade.mp4 -i making_named.mp4 -filter_complex "concat=n=2:v=1:a=1" output.mp4
撮影時いろんなことをやったので、ちょこちょこ書いてみます。
ロゴとエンドロール
VRだと一般にやるべきでない演出なわけですが、今回動画用ということで破綻を諦めて作ってみました。画面上にあるはずのエフェクトが空間に溶け込んでいく様子。ちょっと完璧にはいかずアーティファクトが出ているのは残念。
元々タイトルは置くとしてどこに置くかは悩んでいて、柱に貼る案とかもあったんだけどまぁこれは最適解だと思う。ちょっと驚いてくれると思うし「私達はこういうことをします」という意思提示でもあるわけです。
さて実装は… 大変でした。
2D空間と3D空間を橋渡しするには相対配置を完全に把握する必要があります。しかし**VRカメラの位置を知ることは出来ません。**自前でカメラを置くと負荷になってしまうのでそれは避けたい。どうにかしてカメラ位置を把握する方法が必要でした。
そこで左手からの相対位置を使うことにしました (Capのアイディア)。pickupしてる間は位置が固定されているはずです。正確なカメラの位置はわからないけど、一貫した空間からの相対位置を知ることはできます。
ということでその情報をCustomRenderTextureに渡して「位置がある程度を越えたら泡を生成する」ようにしました。そして描画時にはわかるカメラ位置を使って出来る限りのデコードをします。細かい計算は忘れました…。
float4 loc = tex2Dlod(_Store, float4((gp+0.5+storeOffset)/storeSize, 0, 0));
float3x3 viewMat = transpose((float3x3)UNITY_MATRIX_V);
center = loc.xyz;
center = appQ(invQ(_HeadRot), center - _HeadPos.xyz);
center.z *= -1;
center = mul(viewMat, center) + _WorldSpaceCameraPos;
center.y += (loc.w-1) * 0.1;
float2 uv = float2(rand(gp), (rand(gp+0.5)-0.5) * 0.3 + 0.5);
float logo = tex2Dlod(_Logo, float4(uv,0,1)).a;
if(logo < 0.01) return;
Head
って名前になってるのは最初は頭の位置を使おうとした名残ですね。
あとエンドロール。
普通にadditiveでテクスチャを貼りました。ちなみにこれは私の手書きです。SubstancePainter製。空間にそのまま置くということが出来るのはやっぱり面白いですね。
そういえば書いてなかったけどまぁこれも私の手書きです。Amebientとギリギリ読めつつ読めない感じを狙いました。初回だとぱっと見の印象でAmebientと読んで、2回目で違う読み方にしか読めない、くらい。
「いい感じに空間に溶けそうな文字」を考えて描くのはすごい楽しかったです。
撮影係
撮影はCapにお願いしたわけですが、こう、まぁ、当然大変です。ほんとお疲れさま及びありがとうございました。
映す流れとかは予め決めておいたわけですが、それを映像として綺麗に映すのが難しい。最初普通に歩いてもらってホームビデオ感、とか思ってたけど大分厳しかった。あまり動かないでもらうのが良いな、ということになりました。
そのアングルなどもある程度決まっていたわけですけど、それに毎回合わせるのもとても大変です。特にロール回転は合わせたい。そこで…
空間バミりを置きました。これにVRカメラをぴったり合わせてもらうようにしました。完璧。
ちなみにこのバミりの配置はVRChat上で決めました。pickup付けたバミりを置いといて、いい感じになるように配置。その座標を測ってUnity上で再現。この時にロール角を0にするようにします。
各々に対して3回 (原点・右・上) 測ることで回転も確定させるようにしています。ちなみにカメラに映らないようにしちゃったのでどこを測っているかぱっと見わからないんですよね。まぁそこそこ推測しやすい構造だったので助かった。
そうやってアングルを決めつつ、もう一つ撮影用の機材を導入しました。**FlyingSystem**です。
Making部分を見てわかるようにCapはアレで空中移動して貰っています。ついでに地面のコライダーが抜けるようになっているので柱とかにも当たらなくて便利。10:44でぶつかってるのは配管です。
あと私がMakingで「柱から45度くらいだから」って言っている話は、ちゃんとバミりました。
これでもまぁ安定した撮影には遠くて、Capには物理的にいろいろ頑張らせてしまいました。おかげでいい感じになれたと強く思います。
撮影の様子とタイミング合わせ
撮影時は声を入れないためにDiscordで話しながらやってました。とは言え緊張感はめちゃくちゃあって、現在状態の共有とかを主として話してました。Makingの声が明瞭なのはそういう理由ですね (ちなみに撮影はれいやさんです、影見るとわかる)。
何度か撮ってるんですけど、できるだけ音楽的にしたいというらくとさんの要望から途中から追加されたものがあります。
リズムが丁度となる空間位置のマーカー。そして現在の拍数表示です。
前者は雷とぴったりリズムを合わせて演奏するために。そして後者は次に雷が来るタイミングを正確に予想する為に置かれています。
動画をよく聴いてるとわかりますが、「雷の直前で音を間引く」ことを何回かやっていて。これによって音に動的な変化が生まれてすごく良い感じ。で、そのタイミングを測る為にMainFloorからも見えるようにしたわけです。
ただびっくりしたのが… 最初拍数がほぼみんな一致しているのに後半になると結構ズレてくるんですね、これ…。つまり遊んでいる間に気づかないラグが結構溜まっている場合があるというのを初めて「観測」しました。厳しいね。
なので (同期ラグもあるし) 全体的に各々早めに動かすようにしてました。綺麗なセッションは難しいですね。
おまけ
— Cap.🌓 (@CaptainAyakashi) September 4, 2020![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/9b9d50e9-7603-4fdf-dfbf-6622c0fec27a.png)
…最終的にはいつもの京狐さんで撮影してもらいました。手の位置とかも慣れてるほうが良かったと思うし。
やらなかったことたち
最後に「Amebientでやらなかったこと」を紹介します。これは私のメモたちの中で「なにもなければそのまま」と分類されたものたちです。一部は既に説明していますね。
- Fog
- いらなかった
- 遠近感が無ければ入れる予定だったけど元々空間設定が遠近感を持ってるので十分
- randの精度問題
- 3時間くらい経つと雨粒の落下位置が固定化されてくる
- クリティカルではないので放置…
- 海中のSurfaceが見える
- 水面が見えるのは不思議、高さに応じて描画しないことは出来るはず…
- 消すと綺麗に見えなかった、物体的に見やすいしこれでいくことにした
- FallRainのgroundDepthの計算が雑
- 正直私自身これどこのことかわからない
- 建物の水たまり面
- 床とかが濡れて反射しているのを考えていた
- でも抽象化された波紋とかがあるから必要性は無かったんだと思う
- 海の波
- あるべき、とは思ったけど描画もシミュレーションも厳しそう…
- 穏やかな海ということで… それに波自体は意味を持っていないので
- 中空から降ってくる雨でも音が鳴る
- 見た目がちょっと不安、あと適当に置いた楽器がなってしまうのはちょっと微妙?
- UnderwaterからThunderがくっきり見える
- fadeoutくらいはいれたいかも、って書いてるけど入れてないですね
- 飯盒の可動域
- ぷらぷらして一回転とか余裕でするけどいいのかな
- いいということにした
- SubVisual Mapズレ
- VisualControllerの波紋、位置が微妙にズレてるらしい
- 気づかないので放置
- プレイヤー位置表示
- 各プレイヤーの位置を表示するモニタとかあったらかっこいいし実用的とか思っていた
- 実装する理由が特になかったので無くなった (探せばすぐ人は見つかる)
- 雨の発音の基準の高さ
- メモリが微妙に足りない
- ファンが綺麗に見えない
- 高速回転するファンは回しすぎるとストロボになっちゃう…
- 余裕があったらmotion blur実装してたかもしれない
- 流れる水
- 掬った時に水が溢れる感じのパーティクル出すべきか、と思っていた
- けど作るモチベーションが無かったのでなくなった
概ね「バレないからOK」「必要性が無いのでOK」みたいなところです。こういう演出系はまぁ如何に納得させるかが重要なので、納得さえあれば良いのは確かです。
まぁ雨の基準高とかはやったほうがよかったかな、とは思ってます…。うーむ。
少なくとも今回こうやって「完成」はしたので、それを基準として「何をやるべきか」みたいなことをはそのうち判断できるのかもしれないな、とかちょっと思っています。よくわからないけど。
おわり
これで私の記事は終わりです。作っていて色々考えていたことを思い返してみると本当に沢山あって恐ろしいですね…。
でもそれをちゃんと外に出す機会を作れて本当に良かったです。特に意思の話を細々と書けて良かった。私はいつか見返すことになるんだろうと思う。
昔はこんなこと考えてたなぁ、というよりは「今も変わってないな」という気持ちで振り返りしたいものですね。
読んで頂きありがとうございました。あと3日分、ゆっくり待っててください。