Amebient / 残りの話

22日目です。今までに話さなかった残りの話について書きます。
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Lighting

Amebientは**全空間が繋がっている**ようになってます。「世界全体で1つ」にしたいとか、相互移動がしやすいようにとか、色々あるとは思います。

代わりに各空間は**機能によって**分断されていて、それは自然に生まれた違いです。だから全体のLightingはすごい素直です。

**Directional Lightが3灯**。2つがBake用、1つがRealtime用。全部同じ向きです。

Bake用のライトは「太陽の代わり」と「環境光の代わり」として使っています。太陽だけだとUnderFloor (バケツがあるところ) が暗すぎたので、NoShadowで暗めのライトを置いています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/5cd7642a-afea-da5f-7238-79ee9a399632.png)
Bake設定はCapと私で弄っていた記憶があります。AO入れたのとIndirectIntensity上げたのは私だと思う。

Realtime太陽のレイヤーはEverythingなので建物の影は全て二重に掛かっています。物理的には正しくないですけど案外素直に受け入れられた気がするんですよね。特に**Realtime影の境界がBaked影の境界と混ざっている**のでリアルタイム影のアーティファクト感とかが減っているような気がします。

VRChatのワールドでは影が消されていることが多いと思います。それは描画負荷的には仕方ないことであり**当然**とさえ思いますが、Amebientには**影があって欲しかった**。ここはToonyでもPhysicallyBasedでもない世界だと思っていて (VRChatのことですね)、そしてここは影が存在する世界な気がしていた。

これによって負荷が上がっている部分はあると思いますが (Batchesの1/3)、得られた恩恵は大きいと考えています。これの2枚目とか。
<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">**作戦会議**をしました。 <a href="https://t.co/zzEUpQnkfi">pic.twitter.com/zzEUpQnkfi</a></p>&mdash; Cap.☀ (@CaptainAyakashi) <a href="https://twitter.com/CaptainAyakashi/status/1275849300380131333?ref_src=twsrc%5Etfw">June 24, 2020</a></blockquote> 

&nbsp;
加えてスロープのあるところにはいくつかライトが置いてあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/3791bdd6-70bd-0c9a-75e8-1594b2867b5a.png)
これはEditorOnlyのEmissiveな球です。極端ではありますが、このスロープ部屋の穏やかな雰囲気 (特に一番最初に訪れたときの暖かさ) を直接的に与えることが出来ています。

配置は多少考えてます。「SubFloorからは一切見えない (対比的な冷たさを出したかった)」というのと「各階で見かけが異なるようにしたい」という目的で、UnderFloorからは小さく一灯だけ見える・MainFloorからは階段の上下に見える・屋上からは「屋上を示すランプ」のように**梁の上に置いてある**、っていう配置にしています。たしか。

あとちょっとAreaLightがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/579453ea-b08e-3f65-ad85-a429f6878d4c.png)
Capが置いてくれました。これは大正解。元々外からの光はほぼ入ってなくて暗かったんですけど、このライト群によって「外の明るさ」を認識できるようになった。とても良い。

あとReflectionProbeは各階と各スロープ室、あと上空と**海底** (`0,-100,0`)にあります。海底のReflectionProbeにはSkyboxだけ映っていて、海や泡のMeshRendererにはAnchorOverrideでその場所を指定してます。それが正しいと思ったので。

## PostProcess

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/6e9966a5-05ad-b94c-7db0-2a762ee375db.png)
**本当におすすめしません。**こんな最悪パラメータでよくあんなに安定した色味が出てますよね…。全パラメータを同時に最適化してたので正直ちゃんとした説明は出来ません。

まず**湿度**を出したかった。のでBloomを**Threshold 0・Diffusion 10**で入れました。全力です。そしてアバターを光らせたくは無かったので**Clamp 0.75**になってます。Amebientにある物体はそんなに明るくないんですがアバターに何が入ってるかはわかったものではないですから… (確か私のUnlit髪飾りを見て調節してた気がします)。

そして**Intensity 4**。これは**後に全体の光量を落とすことで相対的にブルーム量を増やす**為です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/c69c202c-7b20-98c1-76c1-25fa7eee1e20.png)
Bloom自体はadditiveで掛かってしまうわけですが、Post-exposureで光量を落とせばblending的に掛けることができます。
Bloomを抜いて光量を概ね合わせるとだいぶぱきっとした感じになりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/72b2c8db-36d2-0154-94bb-5e728627ab71.png)
Bloomのぼかし効果によって空の描画のアーティファクト軽減になっている部分もそこそこあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/7f979ad7-b624-a83c-1213-2995f9550221.png)

&nbsp;
そしてColorGrading…。Amebientは暗い場所とそこそこ明るい場所、どちらもあるのでうまく両立させる必要がありました。Tonemappingは「マジで暗い場所は暗いまま」「中域はちょっと持ち上げる」「明るい部分は少しだけ抑える」感じになってます。

Amebientの空間は全体的にコントラストが低い (Bloomのせいもある) ので、その領域を**いい感じに**見やすくする必要がありました。ぐっと引き伸ばしているイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/7ab94f63-abf2-b7f0-a049-a850a2ffb10d.png)
本当にこうなってるのかな。感覚で調節してるのでよくわかりません。色んな場所を見ながら延々とパラメータを弄っていました。

あと**Temperature -5**はまぁ温度感の調節。**Constrast 20**はちょっとだけ持ち上げるため (この状況下だとあんまり効果無い)。

Trackballsの**Gain**はあんまり意味がない (**Post-exposure 0**の状態にする為かしら)。**Lift**は、暗所でのBloomを持ち上げる為です。上げすぎると暗所が暗所じゃなくなってしまうわけですけど、少しだけ入れることで暗所のコントラストを向上させることが出来ました。

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/HHwhE7ZEvo">pic.twitter.com/HHwhE7ZEvo</a></p>&mdash; phi16 (@phi16_) <a href="https://twitter.com/phi16_/status/1336243799790501889?ref_src=twsrc%5Etfw">December 8, 2020</a></blockquote>

そういえばBloomが全体に広がっているおかげで**逆AutoExposure**みたいになっているのも面白いですね。これが「空間の違い」を引き立てるのに寄与している部分はあるかも。

ちなみにVignettingとChromaticAberrationは[自作のシェーダ](https://qiita.com/phi16/items/49254d25a0e98481ed4f#overlay)で動いてます。元々はPPSでやってました。

## Hierarchy

なんとなく公開。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/5331e6a8-ead6-4b5c-5dd3-a1eb4379df51.png)
**これでもめちゃくちゃ階層化してるタイプ**らしい (これが普通だと思っていた…)。トップレベルにあるのは**5つ**で、World (全体の設定とか世界に関係ないものとか)、Mecha (ギミック系)、Machina (機械類)、Environment (ギミックっぽくないもの)、あとMisc (テスト的に置いたもの) として大きく分類しています。

階層化する際には原則としてTransformがデフォルトのGameObjectに入れるようにしていて、本当に**概念的階層**としての役割だけを持たせています。MachinaのMonitorたちには位置が入ったりはしてます。

大昔は階層化しすぎると再計算とかあるんかなと思っていた時期もありましたけど、なんかそんな様子は一切無いし、何より整理による利益がめちゃくちゃでかいので… ぱしぱし作っちゃっていいんだなという気持ちです。

特に「何かを追加しても見かけ上のHierarchyに変化が無い」のは大きいですね。いつも「Hierarchyパネルの真ん中らへんにシェーダ製エフェクトが並んでる」状態で作業をしていました。安心感。

階層化しようと思えばもっと出来るとは思いますけど、意味のない階層化はすべきではないという点で例えば「海用のシェーダ物体」とかを**まとめたりはしない**ようにしています。位置的関連性は本質的にはどうでもいいし、シェーダ物体群として高々10個が横に並んでいる方がマシです。個人の感想です。

逆に「同じ物体」はできるだけまとめるようにはしていますね。それはそういう関連性がちゃんとあるからです。

&nbsp;
あとおまけに… ちょっと違う話ですけど、prefabの運用について、色々あったような気がします。

一箇所でしか使われないモデルとかをprefab化しても**本来は**特に意味がないんですけど、複数人開発の場合には「変更の箇所が局所的になる」という大きなメリットがあります。ただ、「バルブを動くようにする」ような変更だと元メッシュの変更によってギミックが破壊されることがあって (あった) ちょっと悩んでいた覚えがあります。まぁそんなに変更回数は多くなかったので運用でカバーになってる様子ではあります。

あとPrefab Variantがめちゃくちゃ便利だった。要はオブジェクトの継承みたいな気持ちですね。**缶も鍋もメッシュが違うだけで内部ギミックは同じ**なのでPercussionというprefabを作ってそのメッシュ差し替えとして作られています。加えて同じ物体をいっぱい置いたりもするので**それごとprefabにする**ようにしました。正しい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/15428466-8949-b872-ae4f-0f54280393cc.png)
最近Amebientが壊れたのを治す時にPercussion prefabだけ弄って全てが治ったりもして良かった。まぁ**正しく表現すると正しく振る舞う**という話ですね。

開発についての話はこんな感じですかね…。ちなみにGitHubにリポジトリ置いてえいえいってやってました。

## 20200901

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">&quot;Amebient&quot;が30000visitsに到達しました🙌みなさんありがとうございます！<br>たくさんの方に訪れて頂けて、メンバー共々とてもうれしいです。<br>ところで、ここに偶然Amebientで遊んだときの記録映像があったので、記念に公開しました。<a href="https://t.co/Ss4svddgpq">https://t.co/Ss4svddgpq</a></p>&mdash; Cap.🌥️ (@CaptainAyakashi) <a href="https://twitter.com/CaptainAyakashi/status/1301875423832276992?ref_src=twsrc%5Etfw">September 4, 2020</a></blockquote>

この動画、フェードと最後のMaking部分以外は**編集が一切入ってません**。VRChatのいつものカメラです。

ちなみに動画編集は私がffmpegでやりました。

```sh
# 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
```

撮影時いろんなことをやったので、ちょこちょこ書いてみます。

### ロゴとエンドロール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/5dbc1d08-39ce-45c6-7617-589625d7d6a5.png)
**2Dと3Dの橋渡し。**やりたいですよね。

VRだと一般にやるべきでない演出なわけですが、今回動画用ということで破綻を諦めて作ってみました。画面上にあるはずのエフェクトが空間に溶け込んでいく様子。ちょっと完璧にはいかずアーティファクトが出ているのは残念。

元々タイトルは置くとしてどこに置くかは悩んでいて、柱に貼る案とかもあったんだけどまぁ**これ**は最適解だと思う。ちょっと驚いてくれると思うし「私達はこういうことをします」という意思提示でもあるわけです。

さて実装は… 大変でした。

2D空間と3D空間を橋渡しするには相対配置を完全に把握する必要があります。しかし**VRカメラの位置を知ることは出来ません。**自前でカメラを置くと負荷になってしまうのでそれは避けたい。どうにかしてカメラ位置を把握する方法が必要でした。

そこで**左手からの相対位置**を使うことにしました (Capのアイディア)。pickupしてる間は位置が固定されているはずです。正確なカメラの位置はわからないけど、**一貫した空間からの相対位置**を知ることはできます。

ということでその情報をCustomRenderTextureに渡して「位置がある程度を越えたら泡を生成する」ようにしました。そして描画時にはわかるカメラ位置を使って**出来る限りのデコード**をします。細かい計算は忘れました…。

```hlsl
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`って名前になってるのは最初は頭の位置を使おうとした名残ですね。

&nbsp;
あとエンドロール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/461fb548-b492-5221-1e5e-21a3c584a3ef.png)
普通にadditiveでテクスチャを貼りました。ちなみにこれは私の手書きです。SubstancePainter製。空間にそのまま置くということが出来るのはやっぱり面白いですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/81754e10-f51a-dc7c-ad57-fae733df797c.png)
そういえば書いてなかったけどまぁこれも私の手書きです。Amebientと**ギリギリ読めつつ読めない**感じを狙いました。初回だとぱっと見の印象でAmebientと読んで、2回目で違う読み方にしか読めない、くらい。

「いい感じに空間に溶けそうな文字」を考えて描くのはすごい楽しかったです。

### 撮影係

撮影はCapにお願いしたわけですが、こう、まぁ、当然大変です。ほんとお疲れさま及びありがとうございました。

映す流れとかは予め決めておいたわけですが、それを**映像として綺麗に映す**のが難しい。最初普通に歩いてもらってホームビデオ感、とか思ってたけど大分厳しかった。あまり動かないでもらうのが良いな、ということになりました。

そのアングルなどもある程度決まっていたわけですけど、それに毎回合わせるのもとても大変です。特にロール回転は合わせたい。そこで…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/e23aeece-7f1b-ca60-56b1-ffd98ebc2e17.png)
**空間バミり**を置きました。これにVRカメラをぴったり合わせてもらうようにしました。完璧。

ちなみにこのバミりの配置はVRChat上で決めました。pickup付けたバミりを置いといて、いい感じになるように配置。**その座標を測って**Unity上で再現。この時にロール角を0にするようにします。
![VRChat_1920x1080_2020-08-30_19-00-27.113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/7042fcf7-ea82-9c46-bd61-6c04155dc2f4.png)
各々に対して3回 (原点・右・上) 測ることで回転も確定させるようにしています。ちなみにカメラに映らないようにしちゃったのでどこを測っているかぱっと見わからないんですよね。まぁそこそこ推測しやすい構造だったので助かった。

&nbsp;
そうやってアングルを決めつつ、もう一つ**撮影用の機材**を導入しました。**[FlyingSystem](https://qiita.com/phi16/items/77460565e8afbf692f41)**です。
Making部分を見てわかるようにCapはアレで空中移動して貰っています。ついでに地面のコライダーが抜けるようになっているので柱とかにも当たらなくて便利。[10:44](https://youtu.be/nL-19g6wgU8?t=644)でぶつかってるのは配管です。

あと私がMakingで「柱から45度くらいだから」って言っている話は、ちゃんとバミりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/47c67c7d-83ae-7090-5e04-9b9ae9e11696.png)

これでもまぁ安定した撮影には遠くて、Capには**物理的に**いろいろ頑張らせてしまいました。おかげでいい感じになれたと強く思います。

### 撮影の様子とタイミング合わせ

撮影時は声を入れないためにDiscordで話しながらやってました。とは言え緊張感はめちゃくちゃあって、現在状態の共有とかを主として話してました。Makingの声が明瞭なのはそういう理由ですね (ちなみに撮影はれいやさんです、影見るとわかる)。

何度か撮ってるんですけど、**できるだけ音楽的にしたい**というらくとさんの要望から途中から追加されたものがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88634/3ef5e86d-ffd5-4528-1e08-22817fdb7328.png)
リズムが丁度となる空間位置のマーカー。そして現在の拍数表示です。

前者は雷とぴったりリズムを合わせて演奏するために。そして後者は**次に雷が来るタイミングを正確に予想する為**に置かれています。

動画をよく聴いてるとわかりますが、「雷の直前で音を間引く」ことを何回かやっていて。これによって音に動的な変化が生まれてすごく良い感じ。で、そのタイミングを測る為にMainFloorからも見えるようにしたわけです。

ただびっくりしたのが… 最初拍数がほぼみんな一致しているのに後半になると結構ズレてくるんですね、これ…。つまり遊んでいる間に気づかないラグが結構溜まっている場合があるというのを初めて「観測」しました。厳しいね。

なので (同期ラグもあるし) 全体的に各々早めに動かすようにしてました。綺麗なセッションは難しいですね。

### おまけ

<blockquote class="twitter-tweet" data-conversation="none"><p lang="und" dir="ltr"> <a href="https://t.co/TJNCsp7OAZ">pic.twitter.com/TJNCsp7OAZ</a></p>&mdash; Cap.🌓 (@CaptainAyakashi) <a href="https://twitter.com/CaptainAyakashi/status/1301874705914146818?ref_src=twsrc%5Etfw">September 4, 2020</a></blockquote> 
![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日分、ゆっくり待っててください。