一つのマテリアル内で複数のシェーダーアニメーションをマスクで制御する(Arktoon+T)
概要
シェーダーアニメーションを行うとすれば、基本的にはマテリアルの分割を行う。でも、ゲームエンジン的にはマテリアルを分割するのはパフォーマンス面であまり良くないと言われている。(場合にもよるだろうけど)
そこで丁度 Vket4 というものがあるみたいで、マテリアルを分割しないシェーダーアニメーションについてはある程度形になっていた物が手元にあったから出展してやろうという事になった。……とはいえ、シェーダー自体は Godot 用のフルスペックトゥーンシェーダーとして開発していた物で、ロクに触ったことのない Unity に持っていく過程で一からシェーダーを組むのは骨が折れるから、突貫で Arktoon という MIT ライセンスの Unity 用トゥーンシェーダーに組み込む事に。実験的な機能の実装になるから、本家 Arktoon とは切り離し、派生品として名前空間や GUID は変更した。
Arktoon との差異
機能としては以下の物を追加した。
- 受けるライトカラーの彩度を調整できる機能を追加
- Emission 系にテクスチャのアルファチャンネルを用いたマスクを追加
- Gloss と Reflection についてテクスチャでカラー指定できるように変更
- Gloss の Metallic にマスクを追加
- UV Transform の追加(これに付随して2つめの Emission も追加)
- アニメーション特化のバリエーションとして Emissive Waves を追加
- Common に RenderQueue を追加
Emission のマスクは、 Emission が HDR カラーでの発光に対応した訳だけど、色を維持したまま発光度合いだけ押さえたい時にマスクが欲しくなるから実装した。アニメーションに関しては動画を見てもらうのが一番わかりやすいと思う。
マップの作成手順が複雑だったりやパラメータが多かったりするからこの下でざっと説明するよ。
バージョンは v1.1.0.0 以降(最新版は v1.2.0.0 )で説明するよ。
UV Transform
動画のこの辺り。要するにテクスチャの一部をトリミングしながらスクロールとか回転ができる。
Scroll
( GIF アニメは長いと重いから基本的に編集でループさせてる)
- Scroll Map - スクロールマップ(後述)
- Scroll Speed Map - スクロール速度マップ(後述)
- Scroll Speed - 基準となるスクロール速度
Rotate
- Rotate Map - 回転マップ(後述)
- Rotate Speed Map - 回転速度マップ(後述)
- Rotate Speed - 基準となる回転速度
任意のテクスチャの UV Channel を該当の Channel にすると変形された UV が適用される。
Emissive Waves
動画のこの辺りから。トレースや点滅、色合いの変更といった周期的なアニメーションを行う物。マテリアルを分けたりタイムラインを使ったりせずにアニメーションのバリエーションを出せるようにしてある。
- Texture & Color - Emissive Waves での発光色
Trace
- Trace Map - トレースマップ(後述)
- Use Second Channel - トレースマップの G チャンネルを使うか
Trace Common
- Amount - トレースの時に発光色をどれだけ減衰させるか
- Freq - トレースを繰り返す周波数
- Offset - トレース周期のオフセット
- Amount & Freq & Offset Mask - 上3つの値のマスク( RGB がそれぞれ AFO に対応する)
Trace In Out Ratio
- Ratio - トレースの頭と尻の比率
- Use Ratio Map - Ratio をマップで指定するか
- Ratio Map - Ratio マップ( R チャンネル)
Trace In / Trace Out
- Smooth - フェードをスムース補完するか(オフだと線形補完)
- Use Smooth Map - スムース補完の指定にマップを使うか
- Smooth Map - スムース補完を指定するマップ( R チャンネルを参照、二値)
- Width - フェードを含めた全体の幅
- Softness - フェード具合
- Bias - フェードの指数的変化の値
- Width & Softness & Bias Mask - 上3つの値のマスク( RGB がそれぞれ WSB に対応する)
Trace Unison
- Unison Count - 追加のトレースの数(処理が多くなるから最大5で制限)
- Use Unison Count Map - 追加のトレースの数の指定にマップを使うか
- Unison Count Map - 追加のトレースの数を指定するマップ( R チャンネルを参照)
8bit Value | Reccomend | Unison Count |
---|---|---|
0 ~ | 0 | One |
51 ~ | 77 | Two |
102 ~ | 128 | Three |
153 ~ | 179 | Four |
204 ~ | 255 | Five |
- Freq Multi - 追加トレースの周波数倍率
- Decay - 追加トレースの減衰率
- Offset - 追加トレースのオフセット
- Freq Multi & Decay & Offset Mask - 上3つの値のマスク( RGB がそれぞれ FDO に対応する)
Blink
- Wave Type - 波の形状(コサイン波,三角波,矩形波,鋸歯状波,カスタム波)
- Use Wave Type Map - 波の形状をマップで指定するか
- Wave Type Map - 波の形状を指定するマップ( R チャンネルを参照)
8bit Value | Reccomend | Wave Type |
---|---|---|
0 ~ | 0 | Cos |
51 ~ | 77 | Tri |
102 ~ | 128 | Sqr |
153 ~ | 179 | Saw |
204 ~ | 255 | Custom |
- Custom Wave Map - カスタム波を生成する 1D マップ(後述)
- Blink Invert - 波の明暗反転
- Power - 波を冪乗する指数
- Power Invert - 波の指数反転
Blink Common
- Amount - 点滅の時に発光色をどれだけ減衰させるか
- Freq - 点滅周波数
- Offset - 点滅周期のオフセット
- Amount & Freq & Offset Mask - 上3つの値のマスク( RGB がそれぞれ AFO に対応する)
Blink Unison
- Unison Count - 加算波の数(処理が多くなるから最大5で制限)
- Use Unison Count Map - 加算波の数の指定にマップを使うか
- Unison Count Map - 加算波の数を指定するマップ( R チャンネルを参照)
8bit Value | Reccomend | Unison Count |
---|---|---|
0 ~ | 0 | One |
51 ~ | 77 | Two |
102 ~ | 128 | Three |
153 ~ | 179 | Four |
204 ~ | 255 | Five |
- Freq Multi - 加算波の周波数倍率
- Decay - 加算波の減衰率
- Offset - 加算波のオフセット
- Freq Multi & Decay & Offset Mask - 上3つの値のマスク( RGB がそれぞれ FDO に対応する)
- Normalize - 正規化するか(オフだと 0.0 ~ 1.0 の値をはみ出すとクロップする)
- Use Normalize Map - 正規化の指定にマップを使うか
- Normalize Map - 正規化を指定するマップ( R チャンネルを参照、二値)
Tint
- Tint Map & Color - 色合いを決める 1D マップ(後述)とそれに乗算される色
- Tint Type - どの Tint Map & Color を使うか
- Use Tint Type Map - Tint Type の指定にマップを使うか
- Tint Type Map - Tint Type を指定するマップ
8bit Value | Reccomend | Tint Type |
---|---|---|
0 ~ | 0 | A |
85 ~ | 128 | B |
170 ~ | 255 | C |
Tint Common
- Amount - どれだけ色合いを反映するか
- Freq - 遷移周波数
- Offset - 遷移周期のオフセット
- Amount & Freq & Offset Mask - 上3つの値のマスク( RGB がそれぞれ AFO に対応する)
配布場所とか
リポジトリは GitHub に。何かあったらプルリクとかくれたら対応するかも、とはいえモチベーション次第かな。なんせ Unity が嫌いでね。
あとは Booth に有料サンプルを置いてる。シェーダー本体はコピーレフトの思想に基づいて無償公開するから、このシェーダーアニメーション手法が一般化して、スクロールマップやトレースマップの作成に使えるようなツールが出てくれると嬉しいかも。もちろん勉強代というより寄付目的でサンプルを購入してくれてもモチベーションは上がる。モチベーションが上がるといい事があるかもしれない。
各特殊なマップの作り方
注意事項
- スクロールマップ及び回転マップ はアンチエイリアスを切る事を強く推奨
- スクロールマップ及び回転マップ は OpenEXR の 32bit float モードを強く推奨
- スクロールマップ及び回転マップ の Unity での圧縮設定は None で色は RGBA 32bit にする事
- スクロールマップ及び回転マップ や 変形を適用するテクスチャは Mip Map を生成しない事
RGBA 32bit の画像はビルドサイズが膨らむけどスクロールマップと回転マップは解像度自体はそんなに重要じゃないからテクスチャサイズは小さめでも割と大丈夫。その代わり速度マップの解像度は大きめにして前述の RGBA 32bit のマップを若干トリムするような形を推奨。要は、エンジン側の色変換やスケーリングの時の補完で画像が滲んで予想しない色、すなわち目的じゃない座標の指定が現れる事を極力防ぐということ。それでも誤差が出る時は出るから、できるだけ指定する色が綺麗な数値になるのが理想...まあ、難しいのは分かるけど UV 展開の簡素化やマテリアルの統合とトレードオフという事で。
後はマップやマスク全般に言えることだけど、画像作成時は極力リニアカラースペースで作業した方がいい。 Wrap Mode なんかは画像の淵までマップの値があるかどうかなんかで挙動が変わるから、都度 Repeat とかにしてみてね。
スクロールマップ
R チャンネルを幅、 G チャンネルを高さ、 B チャンネルをグループ ID とする。
グループ ID については複数のスクロール範囲がある場合の判定に必須。 0 でないかつ同じ値であれば同一のスクロール範囲と見做される。範囲が一つしかない場合は 1.0 にでもしておけばいいし、もし範囲が二つなら 0.1 と 0.2 とか別の値にすればいい。 上の注意事項はよく読んでおいてね。
スクロール速度マップ
R チャンネルを速度、 G チャンネルを角度とする。
速度が 0 の部分はスクロールしない。
回転マップ
R チャンネルを中心 U 座標、 G チャンネルを中心 V 座標とする。
これも上の注意事項はよく読んでおいてね。小数点誤差で軸がぶれがちだから、気になる場合は回転の中心 UV 座標を小数点以下の桁数が 3 桁以内になるような場所(全体が 4096px なら 512 の倍数 px とか)に置くことを推奨。
回転速度マップ
R チャンネルを右回転の角速度、 G チャンネルを左回転の角速度とする。
速度が 0 の部分は回転しない。両方の値が存在する場合は打ち消し合う。
トレースマップ
R チャンネルを第一トレースマップ、 G チャンネルを第二トレースマップ、 B チャンネルをマスクとする。
きちんと説明すると長くなるから噛み砕いて説明すると、 8bit カラーの R チャンネルのグラデーションがあったとして、 0 から 255 の値の部分を順番に表示(抽出)するとしよう。( 255 の次は 0 に戻ってまた順に表示する)
このときの抽出範囲を 10 としておくと、時間 0 の時に 0 ~ 10 の部分が表示されて、時間 1 の時に 1 ~ 11 の部分が表示されるといった、帯を動かすようなアニメーションができる。それにフェードイン、フェードアウトとかを加えて実装した物がトレース。
ただ R を 0 から 255 まで見るということは、黒っていうのが R = 0 になってしまうから、マップの黒の部分が全部抽出されるのを防ぐためにマスクが必要になる。つまり R のグラデーションに B でマスクをかけた物をトレースマップとすることができる。ついでに、同じピクセルを一周期の中で二回トレースしたい時に、一つのピクセルに値を二つ書き込めないという問題があるから、それ用に G チャンネルを用意した。
要するに R チャンネルのグラデーションと、 B チャンネルでのマスクを加算合成すればいい。
重ね合わせを使いたい場合はこんなふうに R と G で重ね順を変えた画像をそれぞれ用意して加算合成する。
トレースマップの縁がエンジン側の補完でどうしても汚くなるから光らせる物よりもトレースマップの方を若干広めに作るのがコツ。
グラデーションを滑らかにするために、スクロールマップと同様に 32bit カラーのマップを作ればいいと思いきや、計算過程で小数の桁が溢れ、不正値がノイズとして出てくる事があるから 32bit カラーは非推奨。ビルドサイズが膨れ上がるしね。まぁソフトによっては作業スペースだけ 32bit とか 16bit カラーにしとけば出力画像も綺麗になるみたいな事があるから色々試して欲しい。
余談なんだけど、グラデーションの始点と終点をぴったりくっつけると(円状とか)、画像圧縮で始点と終点の間に変な色が生まれてノイズの原因になったりするから、色相を使う方法や、タンジェントマップを線形にした物を使う方法も考えたりはした。結果としては色相は色相を算出する時点の負荷や近似での誤差があるのでダメ。タンジェントマップは一応綺麗に繋がりはするけど、作業のし辛さと重ね合わせをする時に画像数が増える事を考えると……。結果としては今の形で、出来るだけ高解像度のマップを作るのがいいのかな、という感じで落ち着いた。
1D マップ
作り方もクソもあったもんじゃないけど要するに画像の一番上の行だけを見てる。極端な話、横 100px × 縦 1px の画像でも成立する。でもメモリとかエンジン側の処理を考えると 2 の冪乗 px の方がいいかも。(時代に合わせてこの辺は変わると思う)
カスタム波マップ
要するに明度から波を生成する。
色合いマップ
グラデーションの色を左から順に割り当てる。
発光部分( HDR カラー)へグラデーションを適用する場合、 RGB 全てのチャンネルを使った色が入っていると白くなりがちだから注意。 RGB 全ての値が 1.0 を超えると白になるっていうのは分かるよね。
シルク・レニュー