今日こそやるぞ5日目(いやもう休む)

今日こそやらないページ(サービス潰れて移転五回目)

セルシェーディングのシェーダー作るにあたってライト周りのことメモ殴り書き(またもうちょっとわかったり整理できたら追記していくやつ)

ちょっとセルシェーディングやるにあたって複数のライトがあった場合の処理をちゃんとやろうとして
その結果調べたりしたことやブックマークがそこそこの量になってごちゃごちゃしてきたので
一旦メモ的に書いてみることで脳内整理&今後自分でも見返せるようにする
(とはいえふわふわ認識のままごちゃごちゃ書くやつなので他人が見ても意味なさそう)


まず通常のシェーディングとして陰影を形成して
それを明るい領域、暗い領域になるように絞った後それをマスク化してという流れになるんだけど
それをやるためには一つのPass内で全部の明暗を処理しないといけない(と思う)
(Passをまたぐと前のpassの情報を取得、調整、再利用というのは基本的に無理っぽい重ね書きレベルのことしかできない感じ?)

ここでDeferredとForwardBase&ForwardAddとVertex Litの三種類があるので
セルシェーディングをやるにあたりどれが一番いいのかというのに今さら行き当たった感じの話


・Deferred(まだあやふやだがセルシェーディングには向かないっぽい?)
g-bufferといういくつかの材料(ワールド座標ノーマル情報だとかデプス情報だとか、メタリックだのベースカラーだの)を全オブジェクト描写してしまって最後に一括処理するような感じっぽい?
でこのg-bufferに陰影があればよかったのだけどそれは最後に一括処理されるときにはじめて生成されるっぽい?
(と思っていたのだけどこの記事を書くために調べなおしていると陰影情報のみ描写したものもあるっぽい情報があるので後々ちらべ直す……
 今から調べると話がそれるのでいったん保留)
のでセルシェーディングの材料としての単純な陰影情報としては利用できないっぽい(?)

あと全オブジェクトを一括処理する都合上プロジェクト全体に影響が出る感じで処理の切り替えが必要っぽいとか
(例えばVRChatだと(やったことないけども……)forwardbase設定らしいので多分駄目)
半透明は基本使えないとかもあったりするらしいけどまだあやふやなのと陰影情報の取得には関係なくなってくると思うので割愛


・ForwardBase&ForwardAdd(セルシェーディングに最も向いてる?)
従来通りオブジェクト事に描写していく方式(であってる?)
ただunityの仕様だと(ほかのソフトでも同じ?)一つ目のPass内でとれるライトの情報には限界があるっぽい?

主な参考先
VRChatの皆に手軽に綺麗になってほしくて作ったシェーダーの話

・_WorldSpaceLightPos0
unityが一番重要だと判断したライト(基本的にDirectional Light)が一つだけ情報が取れる

・unity_4LightPosX0
あまり重要でない(重要度二番目以降ということ?)ライトが四つまで情報をとれる

・ShadeSH9
それ以降のライト及びライトプローブにベイクされた情報を取得できる

ということのようなんだけど(それぞれさらに精度とかあるようだけどそこはいったん省く)
unity_4LightPosX0でとれるライトの情報がいまいちしっくりこない……
ビルトインのシェーダー変数だと「重要でない最初の 4つのポイントライト」という説明だけども
・ポイントライトのみ
シンプルなシェーダー記述で試しなおしてみたもののスポットライトも反応しているっぽくて

・重要度
二番目以降という解釈でいいのかもっと後ろのほうなのか……?というのが謎
というのも以前シンプルな構成で試した時に一番近くて強いポイントライトが反応してなかったからなんだけども
これはまた試しなおしたらちゃんと反応してたっぽい……
前の時は自前の記述部分がおかしくてそういう風に見える動作になっちゃってただけとかなのか?というところが確信が得られないので不安

さらにForwardAddというのがあって
こっちを使用すればすべてのライトを順番に処理してくれるようなのだけど
これがもう2Pass目以降になってしまい、1Pass目で描写したものに加算(おそらく)で上乗せする形なので
セルシェーディングの領域分けに利用するのは不可能なんじゃないのかなと思う
(1pass目でセルシェーディングしたものに重ねることはできるけどそれはもう普通のシェーディングが重なったどっちつかずのものになっちゃうのではというか……)
なおかつ先ほどunity_4LightPosX0で処理したライトとの重要度的な比較でどっちが重要だと判定されているわけ?というのもまだわかってないのも痛い……
(仮に_WorldSpaceLightPos0系、ForwardAdd系、unity_4LightPosX0系の順で重要だとされるならば、ForwardAdd無理にでもしないと二番目に重要なライトは処理されていないということになってしまう……)

とかいろいろ知識不足なところもありつつでベストとはいいがたい感じがしてしまう

・Vertex Lit
unity_LightPosition[]を使用することで一つのPASS内で重要なライトを8個まで処理できる!
主な参考先
[Unity] ひとつのPass内で複数のライトを使う
Unityでハマッたこと一覧メモ
ライトにもいろいろ種類があるからややこしい処理をさらに自前でしなきゃいけないものの
情報取得という点では申し分ないしこれ使えばいいじゃん!
とおもったらどうも落ち影を受けるということができないらしい……
(自分でいろいろ書こうという感じで超頑張れば別なのかもしれないけども……)
あがー……


・減衰の取得に関して
unity_4LightAtten0[0~3]
unity_LightAtten[0~7].z
でそれぞれ取得できるっぽいもののそのままで使用できるわけではないというか
これをそのままフラグメントシェーダーの最終出力に突っ込んだとしても
ポイントライトから離れるにつれ暗くなるという本来イメージしている状態にはならないっぽい?
これまた上のほうで出した参考サイトからパクり&改変なんだけども

float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
//ライトの位置を取得
float4 lightPosition = float4(unity_4LightPosX0[i], unity_4LightPosY0[i], unity_4LightPosZ0[i], 1.0);
//ライトの位置と頂点位置を減算して方向を取得(長さをとりたいので正規化はしない)
float3 lightDirection = lightPosition.xyz - worldPos.xyz;
//内積でベクトルの長さをとる
fixed lengthSq = dot(lightDirection, lightDirection);
//ライトの減衰を取得?
float attenuation = 1.0 / (1.0 + unity_4LightAtten0[i] * lengthSq);

という感じでいくつか材料的な値をとってきて最後に計算してやることでやっとイメージ通りの減衰の出力になる
(上の図の場合はattenuationがその出力内容
ただ最後の行の計算の意味がさっぱり理解できてないけどなー!!!

ただポイントライトはこれでいいんだけど
スポットライトの場合が全然わからん……という段階


・ライトのIntensityの取得に関して
_LightColor0だとかで色を取得する際に
ライトの色のrgb x Intensity
という形で混ぜ込まれて渡されるらしい?
なのでIntensityが欲しい場合は
_LightColor0同士で内積を出せばベクトルの長さとしてライトの強さを取得できるっぽい
(これまた上記サイトの内容)
色にライトの強さ混ぜ込まれているというのも知らなかったし
内積は方向が一致してるかしてないかを-1~1で取得できるという解釈だったのだけど
それは正規化されたベクトルの場合で、そうでない場合は同じもの同士をやることで長さ(強さ?)を取得できるというのを知らなかったのでメモしておく



ということまでわかった段階が今

これまでForwardBase(addは使わない)でセルシェーディング書いてきたものの
複数ライトを処理しなきゃという段階になってライトの重要度の判定がいまいちわからんとかもあり
じゃぁちゃんと複数ライト処理できる方法あるらしいからそっちに変更するかとしたものの
それがVertex Litで落ち影もらえないのはダメよな……となり
じゃぁやはりForwardBaseでできる範囲内で頑張るしかないのか??


で、そうなってくると取捨選択の話になってくる気もするけどもForwardBaseだけだと不安な部分について
・二つ目のDirectional LightってShadeSH9とかで考慮してくれてるのか?(してくれてない気がする)
・上でも書いたけどForwardAddで処理される内容と判断されたライトが抜け落ちるとかない?(これは試しなおしたところだと大丈夫そう?以前のシェーダー記述が駄目だっただけ?)
・スポットライトはShadeSH9でしか処理されてないとかっぽいけど大丈夫なもんなのか?
(そもそも浅く広く調べる感じになってしまっているのもあってまだDirectional Lightとpint lightしかシーンにおいてなかったとかもある……)

という点をつぶしていかないとどうにも……

ただ今のところForwardBase以外に選択肢はないように感じるなぁ結局
正直シェーダー自分で頑張って書くというレベルまでやるなら
複数ライトの情報ぐらい制限なく取れるイメージだったけどエンジンごと(もしくはもうここはそういうものでどのエンジンでも同じ?)の制約というかできることできないことあるのね……



というあたりのことをやってたら脳内ごちゃごちゃしてきたので書き出してすっきりしたいという話だった
(前の記事に追記してもよかったんだけどこれだけで長くなりそうだったから別記事としてメモすることにした……)
ただこれ書き始めたところでDeferredでも陰影情報とれるっぽい感じのを見かけたので(勘違いかもだが)まずそれもちょっと調べたいかな(優先度は低めで)

あまりに内容が難しい、もしくはForwardBaseのみでは無理っぽいライト情報は切り捨てる方向で行くしかないのかなーとかも考えないと
わかんないのでぼーっとしてしまうみたいな時間が長くなるばかりかもしれない……