【Unity】Pixelation的な事をやる
前回までのシェーダー学習な流れから逸脱するズェ…!
Pixelationを真似る
てな訳で引き続きA Short Hikeを模倣する。この動画の5分あたりから製作者が「シンプルなモデルでもピクセル化すればそれっぽく見える」と語っている(オレオレ意訳)。
こういった芸術的なピクセル化は英語版Wikipediaで「Pixelation」として紹介されている。俺はモデリングスキルの無さには自信があるので、是非このテクニックをパクって全力で誤魔化していきたい。
さっきの動画の5:50あたりから「テクスチャにレンダリングした奴を別のカメラに映してるよ!」とかいう話があったのだが、その情報だけだとどうすりゃいいのかオラよくわかんねぇぞ…!
困り果ててYoutubeを彷徨っていたら、ちょうどA Short Hikeを引き合いに出して解説している動画があった。サンキューピーター。冒頭に出てくるネコチャンかわええ。
Unity界隈にはPixelationを簡単に実現するアセットもあるっちゃあるのだが、こういうのは自分で組んだ方が勉強になるじゃろ。車輪の再開発は楽しいゾ!
GameObjectの用意
まずは、2台のカメラを用意する。1台目のカメラはシーンを縮小されたテクスチャにレンダリングして、2台目のカメラは縮小されたテクスチャを全画面でレンダリングするらしい。
画像ソフトで大きな解像度の画像を無理やり縮小して引き伸ばしたらジャギジャギになるけれど、あれと原理は同じっぽい。
2台目のカメラの方にCanvasと描画済みテクスチャを貼り付けるRawImageをぶら下げる。こっちは縮小の影響を受けないので、追加でUIが必要だったらここにぶちこめば良い。
1台目のカメラでは、CullingMaskからUIレイヤを外して表示対象外に。
2台目のカメラは、PerspectiveではなくOrthographicに。きっと1台目のカメラが描いたテクスチャを、遠近感無しで表示せないかんのだろう。
あと、2台目の方ではCullingMaskをUIレイヤーのみにしといたる。
Canvasはいつもどおり、Scale With Screen Sizeに設定して画面サイズに追従するようにしとく。ここの設定たまに忘れて、WebGLとAndroidとかでUI配置がガバるんだよな。
最後に、レンダリング結果を書き込むRawImage。これも画面全体にストレッチするような設定にしておけば画面サイズを変えても大安心だろう。
とりま登場人物としては以上になるので、あとはスクリプトを組んでいく。
Bolt(C#)スクリプトの作成
たぶんUnity界隈ではマイノリティだが、俺はBolt中毒なので極力Boltに寄せて書いていく。もしC#で書く場合は元動画を参考にしてほしいゾ!
まず、enumを用意する。なんか現段階のBoltだとenum作れないらしいので、ここだけはC#のスクリプトで用意せざるを得ない。くやしい。
1台目のCameraにBoltのマクロくっつけて、変数を設定する。
modeはさっきC#で組んだenum値を設定。どうやら、ここの設定によって1台目のカメラが書き込むテクスチャサイズの決定方法を変更している模様。
「Resize」にするとtargetScreenSizeWidth/Heightに強制的にテクスチャをリサイズするので、screenScaleFactorの値は無視される。こちらは、画面解像度は全く関係ない。
「Scale」にすると逆にtargetScreenSizeWidth/Heightの値は無視されて、現在の画面解像度をscreenScaleFactor(係数)で割った値になる。つまり、この値が1だったら解像度そのまま(ピクセル化なし)になるし、値を大きくすればそのぶんテクスチャサイズが小さくなるので、最終的な画素もどんどん粗くなっていく。
displayには、先程2台目のカメラCanvas内に作ったRawImageをそのまま入れてやる。こいつに描画済みのテクスチャが貼られる。
StartとUpdate部分。Initイベント内でPixelationの設定をするのだけれど、画面の解像度が動的に変わったときを考慮した造りになっている。Gameビューの解像度を変えた時に、Initが走るような動きになる。
Initの頭で、screenWidthとscreenHeightを設定。こいつらはピクセル単位のスクリーン幅/高さだ。
次に、modeに応じてレンダリング先のテクスチャサイズを決定する。
GetTextureWidthの中身はこう。ResizeだったらtargetScreenSizeWidthを返すし、ScaleだったらScreen.width/screenScaleFactorを返す。
GetTextureHeightもだいたい同じ実装。
RenderTextureを作ったあとは、そいつのfilterModeをPointにするとピクセルがブロック状に表示される。あとantiAliasingを1にすることで、アンチエイリアスを意図的にOFFできるらしい。
そんなこんなで色々設定したRenderTextureをCamera.targetTextureに設定するとカメラの出力先になり、さらにdisplay(RawImage)のテクスチャとしてそいつを設定すれば完了。
動作確認
Resizeだと、targetScreenSizeWidth/Heightを参照するので、そこの値に応じた粗さになる。
Scaleだと、screenScaleFactorの値に応じた粗さになる。
ScaleでscreenScaleFactorを1にすると、もとの解像度そのまんまになる。
2台目のカメラにUIを追加しても、そいつはジャギらない。
こうやって眺めてみると、テクスチャやマテリアルの雑さとかがマイルドになって割といい感じに見えてるのではなかろうか。
さて、次はどんな要素をパクってみようかなぁ。(もはやシェーダーとは関係なくなりつつある)