URPでマトリックスコードシェーダー

![shader_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/93b7886f-d24d-0278-6e41-302f19edf149.png)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">マトリックスのやつ<br>公式サイトを参考にShaderGraphで書いてみました<a href="https://twitter.com/hashtag/unity?src=hash&amp;ref_src=twsrc%5Etfw">#unity</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://twitter.com/hashtag/shader?src=hash&amp;ref_src=twsrc%5Etfw">#shader</a> <a href="https://t.co/1N0abj5Wr3">pic.twitter.com/1N0abj5Wr3</a></p>&mdash; おうどん (@oudon) <a href="https://twitter.com/oudon/status/1448525941370593284?ref_src=twsrc%5Etfw">October 14, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

マトリクスの新作が発表されたので
公式のマトリックスコードを参考にURPのShaderGraphで模写してみました。
雰囲気はでてると思います。

# ソースコード
https://github.com/oudont3/MatrixCodeShader

- Unity 2019.4.16f1
- URP ShaderGraph

# 概要
元となる文字の8×8のテクスチャシートからランダムに文字を抽出、
一枚の板ポリに複数列下へ流れるようにします。

おおまかな流れとしては以下の通りです。

- テクスチャシートを用意
- Shader作成
    - シートの中からテクスチャを拾う
    - 複数行列分、ランダムに表示できるようにする。
    - ランダムの調整
    - 文字のスクロール
    - emissionとfadeoutの調整
    - 複数列で、文字の並びとスクロールタイミングをズラす。
- オブジェクトの配置

# テクスチャシートの作成
![texture-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/95c5f14c-6d0a-29c7-7268-732b8f8cccb5.png)


なにより都合の良いテクスチャがなかなか見つからなかったので
やり方は色々ありそうですが、今回はPhotoshopで作成しました。

まずは1文字ずつpngに書き出して
コンタクトシートIIで読み込み・等間隔に整列して完成です。

せっかくなのでphotoshopのスクリプト書いてみました。

## レイヤー作成
テキストに指定した文字分のレイヤーを作成・センタリングします。
後続のスクリプトのため、レイヤー名に「文字.png」という名前をつけています。
あとで縮小されますが、とりあえず48pxのサイズで作成してます。

```javascript
function layerMoveToCenter(layer) {
    const docWidth = activeDocument.width;
    const docheight = activeDocument.height;
    const bounds = layer.bounds;
    const layerWidth = bounds[2] - bounds[0];
    const layerHeight = bounds[3] - bounds[1];
    layer.translate(-bounds[0] + docWidth / 2 - layerWidth / 2, -bounds[1] + docheight/2 - layerHeight/2);
}

function resetLayers()
{
    var doc = activeDocument;
    for(var i=0; i<doc.artLayers.length; i++) {
        var lname = doc.artLayers[i].name;
        if (lname.substr(-4) == ".png") {
            doc.artLayers[i].remove();
        }
    }
}

function createTextLayer(text) {
    var doc = app.activeDocument;
    var layers = doc.artLayers;
    var l = layers.add();
    l.name = text + ".png";
    l.kind = LayerKind.TEXT;
    l.textItem.contents = text;
    l.textItem.size = 48;
    l.textItem.font = "Meiryo-Bold";
    l.textItem.justification = Justification.CENTER;
    l.textItem.color.rgb.red = 0;
    l.textItem.color.rgb.green = 0;
    l.textItem.color.rgb.blue = 0;
    l.textItem.horizontalScale = 100;
    l.textItem.verticalScale = 100;
    return l;
}

var text = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ";

resetLayers();

for (var i = 0; i < text.length; ++i) {
    var target = text.charAt(i);
    var layer = createTextLayer(target);
    layerMoveToCenter(layer);
}
```

ちなみに作成したスクリプトは
「Photoshop.exeがあるフォルダ」→「Presets」→ 「Scripts」
に管理者権限で配置してphotoshopを再起動すると利用できるようになります。

新規作成→スクリプト実行でレイヤーが生成されると思います。

## レイヤーをファイルに書きだす
次に生成したレイヤーを1枚ずつファイルに書き出します。
これはこちらのスクリプトをそのまま利用させてもらいました。
[ps-export-layers-to-png-jsx](https://gist.github.com/tomekc/2892034#file-ps-export-layers-to-png-jsx-L65)

接尾辞が「.png」という名前のレイヤーだけを保存してくれます。
デフォルトの保存先はPSファイルと同階層の「out」ディレクトリとなってます。

## コンタクトシートIIでシート化
先にやっとけなんですが
8x8のシートを作成するので、outフォルダ内の文字pngを64個になるよう選定します。
それから「ファイル」→「自動処理」→「コンタクトシートII」
先ほどの「out」フォルダを指定して、サイズ・間隔を調整して実行するとシートが生成されるので
背景の透過・文字を白にして、任意にぼかしたりして完成です。

![texture-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/85997bd9-9523-a1e2-d7bb-08ff5b957ba2.png)

# ShaderGraph作成

## シートの中から1枚拾う
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/e0203fa9-90b7-a006-f6cf-891afd0bf2e4.jpeg" width=512>

まずは1文字につき以下のように番号を割り当てるイメージで
index番号(↑図の赤い箇所)を指定すると該当するテクスチャが取得できるようにしてます。
![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/c581b668-be6e-8752-1dbe-f6e1bbb49e7a.png)

とりあえず1文字だけ表示するのですが
まずuv値を8で割るだけで左下の文字だけが表示されると思います。

```
1.0 / 8.0 = 0.125
(0,0) ～ (0.125, 0.125)
のuv座標の絵を取得するイメージ。
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/7f038533-d9d8-5534-7ff1-bd7c53c10e0b.png" width=512>

あとはindexからuvの移動量を計算してやれば
希望のindexのuvの絵を取得できるようになります。

- x軸への移動量は番号/8の余り
- y軸への移動量は番号/8の商。
- これらをuv座標に変換(8.0で割る)したものを↑に加算

例えば番号が8だとすると

```
x軸の移動量 = 8/8の余り0、
y軸の移動量 = 8/8の商1
となり
(0.125 * 0, 0.125 * 1) ～ (0.125 + 0.125 * 0, 0.125 + 0.125 * 1)
↓
(0, 0.125) ～ (0.125, 0.25)
のuv座標の絵を取得するイメージ。
```

後ほどindexにランダムな値をセットするようにします。

## 複数行列分、ランダムに表示できるようにする。
次に出力にn×n分文字を表示できるようにします。
仮に3×3分表示する場合、このようなイメージです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/8bfef8ad-a349-ceeb-2183-695d3852d66b.png">

ノードは以下の箇所です。赤部分がそれぞれuvとindex番号のinputになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/ad987abc-934a-1549-031c-108d5d3551be.png" width=512>

図の中ではShowCodeMatrixがn×nのvector2パラメータです

まず上部でuvの計算をするのですが
uvを3等分してそれぞれの0～1で渡せるようにすれば完了です。
この辺りは[楽しい！Unityシェーダー お絵描き入門！](https://docs.google.com/presentation/d/1NMhx4HWuNZsjNRRlaFOu2ysjo04NgcpFlEhzodE8Rlg/edit#slide=id.g3741eb4de0_0_19)が非常に参考になります。

またこれも事前にやっとけなんですが、ここで文字を反転してます。

下部も同様マスごとに乱数を生成できるように
3×3の各マス毎に乱数生成(0～63)→整数部をindex値として渡してます。
以上で基本構造完成です。

## ランダムの調整

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/9cbf1245-6d2d-25ce-be22-e4426b4ef6a6.png" width=512>
もう少しランダム性を強化する為オブジェクト座標のxyzを足し合わせてseedに加えてます。(適当)
一定時間ごとに変化していたので同じように調整しています。
(突き詰めると実際は変化のタイミングが文字単位で異なるようです)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/1be69cd7-fcbd-e1e6-ac54-9fb0201774e8.gif" width=256>

## 文字のスクロール
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/6558d94c-a67f-a027-eed1-90a84b2252dc.png" width=512>

スクロールと言いつつAlphaでフェードインしながら徐々に表示するようにしています。
元の絵を見た感じ単純なuvスクロールというよりドットアニメーションの動きがイメージ近かったので
文字単位でFadeInするようにしています。

ここではy軸のマス毎にTimeを加算して少数部をとる(fraction)ことで
0～1間で値を変化させてグラデーションを表現しています。

## emissionとfadeoutの調整
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/e9ecc813-ce8c-c09b-7f10-14fba946624f.png" width=512>

上部のフェードアウトは自然にかけ、調整できるようにしてます。
同様に下部の方の発光も影響度を調整できるようにしてます。
またEmitはcolor側、Fadeはalphaにつなぐようにします

## 複数列で、文字の並びとスクロールタイミングをズラす。
後は複数列にいい感じに表示する様、マスのx軸のindexをseedに乱数でいろいろ調整します。

- 最下部のノード群で列毎の出力の有無を調整
- 同様にスクロールのタイミングも列毎にズレるようランダム値を加える

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/daa06da5-4731-931d-b417-66e9c5266b17.png" width=512>

# オブジェクトの配置
タイトルの絵はshaderを適用した板ポリを複数毎、ズラして配置しるだけです。
後はpostprocessの力です。

あくまでuvに沿ってアニメーションしますが
色んなオブジェクトに適用しても面白そうです。

# 参考

## Photoshopスクリプト関連
- [Photoshop Scripting のスニペット集](https://knooto.info/photoshop-scripting-snippets/#%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%81%AE%E5%B9%85%E9%AB%98%E3%81%95%E3%81%AE%E5%8F%96%E5%BE%97)
- [Photoshopでテキストレイヤーを作成するスクリプト](https://blog.goo.ne.jp/m4g/e/bf02ab652842bcebae2f1e0af3b75741)
- [ps-export-layers-to-png-jsx](https://gist.github.com/tomekc/2892034#file-ps-export-layers-to-png-jsx-L65)

## Shader関連
- [楽しい！Unityシェーダー お絵描き入門！](https://docs.google.com/presentation/d/1NMhx4HWuNZsjNRRlaFOu2ysjo04NgcpFlEhzodE8Rlg/edit#slide=id.g3741eb4de0_0_19)