しゅみぷろ

プログラミングとか

水面を作ってみた

はじめに

github.com

サンプルは上のリンクから。

madewithunity.jp

こちらで紹介されている波紋シェーダーが美しいと思ったので、似たようなものを実装してみました。 最近イカやゼルダゼノブレイドやHorizon Zero Dawnやモンハンやらで忙しくてLOST SPHEAR買えてないので今度買います。

実装した水面シェーダーはこんな感じになりました。 動画の方は低画質だと見辛いので、設定で画質をHDにすることを推奨します。

f:id:es_program:20180112005401g:plain:w560


水面シェーダーデモ1



水面シェーダーデモ2


足の動きで波を発生させているのですが、動きの量に応じて発生する波の強弱を変える等の調整をすればもっと良くなりそうな気がします。

実装方法

events.unity3d.jp

「水面 シェーダー」とかでググるといっぱい出てくると思うので、概要だけメモっておきます。 特に、Unite2017の講演の『スマートフォンでどこまでできる?3Dゲームをぐりぐり動かすテクニック講座』を見るだけで実装できると思います。

実装手順としては

  1. 反射をレンダリングするカメラを用意する
  2. 反射を投影しつつ法線に応じて歪めるシェーダーを書く(床面に適応させる)
  3. 波動方程式を解いて法線としてシェーダーで読めるようにする
  4. 波動方程式の入力を足の動きに合わせて与えるようにする

といった感じです。

反射をレンダリングするカメラを用意

f:id:es_program:20180109214538p:plain:w600

こんな感じでカメラを用意してやります。上のカメラアイコンの位置にメインカメラを、下のカメラアイコンの位置に反射用カメラを配置しています。 メインカメラの位置は動的に変わる可能性があるので、反射用カメラも反射を描画する位置に動的に移動するようスクリプト等で制御する必要があります。

反射を投影しつつ法線に応じて歪めるシェーダーを書く(床面に適応させる)

DepthShadowの描画の応用です。要するに投影テクスチャマッピングを施します。 詳細は別記事にて書く(次の記事で書きたい)ので、今回は実装のみ簡単に書いておきます。

投影を行う場合、投影する側とされる側で空間を統一して、投影位置を決定する必要があります。 反射で描画されるそれぞれのオブジェクトは反射用カメラの変換行列を用いて変換され、レンダリングされます。 次に、メインカメラで水面のオブジェクトを描画する際に、頂点を反射用カメラの変換行列を用いて変換します。 このProjection空間に変換された同次座標系に属する頂点を通常座標に直し、テクスチャ座標に変換すれば、実質的に反射用カメラでレンダリングされたオブジェクトと同じ空間に属することになります。 同一空間上の場所を得ることができれば、その座標のピクセルカラーをフェッチして描画に使うのみです。

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 vertex : SV_POSITION;
            float4 ref : TEXCOORD1;
        };

        sampler2D _MainTex;
        float4 _MainTex_ST;
        sampler2D _RefTex;
        float4x4 _RefVP;
        float4x4 _RefW;

        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.ref = mul(_RefVP, mul(_RefW, v.vertex));
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            return o;
        }
        
        fixed4 frag (v2f i) : SV_Target
        {
            return tex2D(_RefTex, i.ref.xy / i.ref.w * 0.5 + 0.5);
        }

コードは反射を投影するシェーダーの一部です。_RefTexには反射を描画したテクスチャを、_RefVPには反射用カメラのVP行列を入力しています。_RefWは反射用オブジェクトのローカル変換行列です。 頂点シェーダーで_RefVPを用いて頂点を反射用カメラのProjection空間へと座標変換し、フラグメントシェーダーで通常座標変換 + テクスチャ座標への変換をしています。

f:id:es_program:20180109233123g:plain:w400

これで、カメラの移動や反射用オブジェクトの移動・回転・拡縮があっても違和感のない投影が行えます。

で、あとは法線マップを参照して歪ませるコードを足していきます。

            float2 offset = bump * _BumpAmt * _RefTex_TexelSize.xy;
            i.ref.xy = offset * i.ref.z + i.ref.xy + _UVOffset.xy;

Unity標準のガラスシェーダーを参考にしました。_BumpAmtは外部から与える影響量の係数、_UVOffsetは外部から与えるUV座標調整用の変数です。 法線で参照する反射用テクスチャの座標をちょっといじっているだけです。

f:id:es_program:20180109234614p:plain:w400

波動方程式を解いて法線としてシェーダーで読めるようにする

波動方程式を解いて、波の高さをテクスチャで取ってこれるようにします。 詳細は記事上部で紹介したUnite2017の講演資料を参照して下さい。死ぬほどわかりやすいです。

慣れていないと偏微分見ただけで吐き気を催すかもしれませんが、こういった画像処理の場合x,y方向の偏微分は隣接するテクセルの変化量(差)であり、要するにただの引き算です。 時間に関する偏微分も以前のフレームで描画したテクスチャを参照し、変化量を求めるだけです。

f:id:es_program:20180112005401g:plain:w600

記事の頭で貼ったGifの左の赤い部分に注目して下さい。これは波動方程式を解くことでえられた波の高さを格納するテクスチャをデバッグ用に表示しているものです。 赤い理由は、最適化のためR要素のみを持つテクスチャを利用しているためです。

因みに左上の白い部分は波立たせるための入力用テクスチャをデバッグ用に表示したものです。見辛いですが、所々黒い点が入力されています。Gifエンコードの関係上、少しグレーであとが残っちゃてますが本当はグレーのあとは残りません...。

次に、このテクスチャに格納された波の高さ(ハイトマップとして扱う)を法線として解釈し、反射用テクスチャを歪ませるようにします。 ハイトマップから法線マップを算出する際にも実は偏微分を解いています。

esprog.hatenablog.com

以前の記事で紹介した、このハイトマップから法線マップを算出する処理がそのまま使えます。 こうして取ってきた法線を先ほどの水面シェーダーのbumpとして入力してやれば、このプロセスは完成です。

波動方程式の入力を足の動きに合わせて与えるようにする

f:id:es_program:20180110000311p:plain:w400

足にColliderを付け、InkPainterのCollisionPainterを改造したスクリプトを付けました。

GitHub - EsProgram/InkPainter: Texture-Paint on Unity.

InkPainterでペイントを行う場合、今回のケースだと1フレームに複数回、入力用のテクスチャを作るためだけに描画処理が走ることがあります。これは1回の処理で済ませられるよう最適化すべきですが、とりあえず今回は動けばいいので最適化等はしていません。

最後に

こういうものが比較的迷わず作れてしまうくらい資料や環境が整ってるのがUnityの良いところですね。 次はよく使う変換手法について纏められたらと思います。

© UTJ/UCL

Splatoon2の自動ドット打ちをもうちょっと簡単にしてみた

はじめに

esprog.hatenablog.com

上記記事でSplatoon2のイラスト投稿でドット打ちを自動化する方法を紹介しました。

色々な方から「できたー」といったお声を頂戴するようになりました。

しかし、「できなかった」というようなお声も頂きました。

ということで今回は、もっと多くの人が簡単に使えるようにしてみましたというお話。

様々な更新を取り入れたりしたので、以前の記事とはかなりやり方が異なります。ご注意下さい。

必要なもの

  1. Teensy 2.0 ++
  2. WinAVR
  3. Switch-Fightstick
  4. Teensy Loader

Teensy 2.0 ++は、購入する必要があります。

Teensy 2.0 ++は最後の最後まで必要にならないので、念のため以降記述する「自動ドット打ち手順」でJoystick.hexの出力まで成功したら購入するので良いと思います。

環境づくり

必要なソフトウェアのインストールやダウンロード手順について書いていきます。

WinAVR

WinAVR download | SourceForge.net

上記リンクより、WinAVRのインストーラーをダウンロードして実行します。

f:id:es_program:20170913115431p:plain:w400

インストールの段階でPathを通しておくと楽です(「Add Directories to PATH (Recommended)」にチェックが入っているかを確認する)。

f:id:es_program:20170913115436p:plain:w400




Switch-Fightstick

https://github.com/EsProgram/Switch-Fightstick/archive/1.2.0f.zip

上記リンクから、zipファイルをダウンロードして解凍します。

解凍したら、「Switch-Fightstick」フォルダが出てくると思います。

フォルダ名の末尾に数字がついていると思いますが、バージョンによって異なるので気にしないでください。

f:id:es_program:20170913122613p:plain:w400




Teensy Loader

Teensy Loader app for Windows 7 and Vista

上記リンクから「teensy.exe」をダウンロードしたら完了です。

f:id:es_program:20170913124234p:plain:w400




自動ドット打ち手順

大まかな流れはこのようになります。

  1. 320x120のサイズで白黒画像を作る
  2. 「Switch-Fightstick」フォルダー内の「Image2Hex.exe」に白黒画像をぶち込む
  3. PCとTeensyを接続し、「Switch-Fightstick」フォルダー内の「Joystick.hex」を書き込む
  4. TeensyとSwitchを接続し、ドット打ちが終わるまで待つ

以下でそれぞれ詳細に説明していきます。

1.320x120のサイズで白黒画像を作る

f:id:es_program:20170904212911j:plain:w320

こんな感じで、320x120ピクセルで白黒の画像を用意します。書くなり加工して作るなり好きにして下さい。 因みに画像はJPEGPNGで保存しておくことをオススメします。

2.「Switch-Fightstick」フォルダー内の「Image2Hex.exe」に白黒画像をぶち込む

「Switch-Fightstick」フォルダー内に、「Image2Hex.exe」があるので起動します。

f:id:es_program:20170922214130p:plain:w400

起動したときの画面は下の画像のような感じです。これに、白黒画像をドラッグ&ドロップします。

そうすると自動的に変換が始まります。

「InvertColor」にチェックを入れておくと白黒が反転します。チェックを入れても「Image2Hex.exe」に表示される画像は変化しません。(紛らわしいので要改善?)

f:id:es_program:20170922214218p:plain:w400

変換に成功するとこんな感じに、「Success」と表示されます。

f:id:es_program:20170922214425p:plain:w400

「Switch-Fightstick」フォルダ内に「Joystick.hex」というファイルが出来上がってると思います。

3. PCとTeensyを接続し、「Switch-Fightstick」内の「Joystick.hex」を書き込む

PCとTeensyを接続し、Teensy LoaderでJoystic.hexを書き込んでいきます。

Teensy Loader(teensy.exe)を起動し、「File -> Open HEX File」から、「Switch-Fightstick」フォルダ内の「Joystic.hex」を選択します。

f:id:es_program:20170913130216p:plain:w400

PCと接続しているTeensyの黒ポチのボタンを押して、プログラム書き込みモードにします。

f:id:es_program:20170913125908p:plain:w400

あとはTeensy Loaderで「Operation -> Program」、「Operation -> Reboot」の順に操作し、書き込みが完了すればこの工程は終了です。

4. TeensyとSwitchを接続し、ドット打ちが終わるまで待つ

SwitchでSplatoon2のイラスト投稿画面までいき、ペンのサイズを最小にしておきます。 TeensyとSwitchを接続し、あとは出来上がるのを待ちます(Switchはドッグに挿した状態で、TeensyはドッグのUSBポートにつなげれば大丈夫です)。

最後に

前回紹介した記事と違う部分は

  • Pythonが不要になった
  • GIMPで画像を変換する手間がなくなった
  • USB Type C アダプタが無くてもドッグにTeensyを直接挿し込めばよくなった
  • ドット打ちが高速化された

くらいでしょうか。

画像作ったらImage2Hex.exeにぶち込んで、Teensyに書き込んでSwitchに接続するだけなので、かなり簡単になったと思います。