無限に数式を自動生成するシェーダーガチャつくってみた

# はじめに

あけましておめでとうございます。
お正月といえば初詣のおみくじですが、
実行する度に違う映像が出てくるシェーダー（数式）ガチャをつくってみました。

シェーダープログラムを実行できるProcessing上で、
プログラムが実行される度にシェーダーの数式を生成します。

![shader.gif](https://qiita-image-store.s3.amazonaws.com/0/197729/7892875d-a4c9-2305-873a-e2db055cfd33.gif)

簡単に試せます

# 目次

- まずは数式生成だけを試してみる
- Processingで実行
    - 環境構築
    - ソースコードの設置
    - 実装&実行
    - どのように数式を生成しているのか

# まずは数式生成だけを試してみる

数式の生成コードをこちらでweb上で実行可能なようにまとめてみました。
python2 [https://paiza.io/projects/dKRwcY0D_wWGkMMa1rNIAw]
(https://paiza.io/projects/dKRwcY0D_wWGkMMa1rNIAw)
まずはこちらのサイトを開き、実行ボタンを押してみましょう。
しばらくすると、出力のところに以下のようにシェーダーの数式が吐き出されます。

```
============================
        #ifdef GL_ES
        precision mediump float;
        #endif
        uniform vec2 resolution;
        uniform float time;
        void main() {
            vec2 uv = -1. + 2. * gl_FragCoord.xy / resolution.xy;
            gl_FragColor = clamp( abs( vec4(
               cos( time * 5.) ,
               uv.y * sin( sin( log( sin( fract( uv.x * sign( sqrt( floor( exp( abs( sin( uv.y * floor( log2(  uv.x * uv.y + time  ) ) - floor(uv.y * floor( log2(  uv.x * uv.y + time  ) )) * time) ) ) ) ) ) - floor(uv.x * sign( sqrt( floor( exp( abs( sin( uv.y * floor( log2(  uv.x * uv.y + time  ) ) - floor(uv.y * floor( log2(  uv.x * uv.y + time  ) )) * time) ) ) ) ) )) ) * time) ) ) * time) - floor(uv.y * sin( sin( log( sin( fract( uv.x * sign( sqrt( floor( exp( abs( sin( uv.y * floor( log2(  uv.x * uv.y + time  ) ) - floor(uv.y * floor( log2(  uv.x * uv.y + time  ) )) * time) ) ) ) ) ) - floor(uv.x * sign( sqrt( floor( exp( abs( sin( uv.y * floor( log2(  uv.x * uv.y + time  ) ) - floor(uv.y * floor( log2(  uv.x * uv.y + time  ) )) * time) ) ) ) ) )) ) * time) ) ) * time)) ,
               uv.x + time ,
               1. ) ),0.,1.);
        }
============================
(878, 'characters code')
```
生成された数式の文字数も同時に表示されます。

シェーダーにはいくつか種類がありますが、
フラグメントシェーダーは基本的に　gl_FragColor = vec4(R,G,B,A);のように
RGBAのそれぞれの色を計算する数式でできています。

このままだと意味が分かりませんが、出力されたコード(=====で囲まれた部分)を[GLSL sandbox](http://glslsandbox.com/e)などのwebサービスに貼り付けると、映像を確認することができます。

<img width="1421" alt="sandbox.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/197729/bcfda08a-3997-93de-6920-a2fb69561dcc.jpeg">
正しく実行できるシェーダープログラムだということが分かります。


# Processingで数式ガチャをつくる

コードを生成できることが確認できたので、次はProcessing上でこれを実行したいと思います。

## 環境構築

windowsでもmacでも大丈夫です。
Processingをインストールします。

Processingのダウンロードはこちらから
[https://processing.org/download/](https://processing.org/download/)

続いてPythonモードのインストール(こちらのリンクが画像付きで分かりやすいです)
[https://pycarnival.com/processingpy1/](https://pycarnival.com/processingpy1/)

## ソースコードの設置

Processingのプロジェクト(pydeファイル)が保存されるディレクトリと同じ場所に、
[https://github.com/mizumasa/pyShaderGenerator]
(https://github.com/mizumasa/pyShaderGenerator)
こちらからダウンロードしたソースコードを丸ごと置きます。

dataフォルダ、shader_lib.py、などのファイルがプロジェクトファイル(.pyde)と同じ階層になるようにしてください。

## 実装&実行

実行するpydeファイルには、以下のように実装します。

```
import shader_lib

def setup():
    global sd
    size(600, 600, P2D)
    global code
    code = shader_lib.makeNewShader()
    print(code)
    shader_lib.saveShader("data/sample.glsl",code)
    sd = loadShader("sample.glsl")

def keyPressed():
    if key == "s":
        global code
        timeStamp = shader_lib.getTimeStamp()
        shader_lib.saveShader("data/"+timeStamp+".glsl",code)
        save("data/"+timeStamp+".png")

def draw():
    global sd
    sd.set("time", millis() / 1000.0)
    sd.set("resolution", float(width), float(height))  
    shader(sd)
    rect(0, 0, width, height)
```

そのまま実行ボタンを押せば自動でシェーダーが生成されます！
windowsだと以下のような感じのフォルダ構成、実装、実行結果になります。

![thumb_windows.png](https://qiita-image-store.s3.amazonaws.com/0/197729/88b5995e-03a9-ff6f-c42a-d423821c0199.png)


## どのように数式を生成しているのか

詳しいことはここでは書きませんが、数式を生成するために、枝分かれする木のように数式を繋げる処理を行なっています。そのため、基本的に同じ数式はほぼ二度と出てこないような仕組みになっています。

<img width="506" alt="code2.png" src="https://qiita-image-store.s3.amazonaws.com/0/197729/ceb16b0b-042d-784f-927a-2e027db63d2a.png">

何回かやってみて、気に入ったものはsキーを押すとdataフォルダの中に
キャプチャ画像がシェーダーコードと一緒に保存されます。

![thumb_mac.gif](https://qiita-image-store.s3.amazonaws.com/0/197729/17a567f6-fcd0-1cb3-effc-5d7263f1fafb.gif)


### おまけ
これを発展させていくと、さらに長く、複雑なコードを生成することも可能です。
<img width="480" alt="charactor7.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/197729/f0062fb3-3459-9f00-0d3b-3d15a90b9282.jpeg">

強化学習を組み合わせてさらに生成を賢くすることも実験中です。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">強化学習による実験。アニメーションのファイルサイズが大きくなるようにシェダーコードを変化させる。<a href="https://twitter.com/hashtag/glsl?src=hash&amp;ref_src=twsrc%5Etfw">#glsl</a> <a href="https://twitter.com/hashtag/opengl?src=hash&amp;ref_src=twsrc%5Etfw">#opengl</a> <a href="https://twitter.com/hashtag/generative?src=hash&amp;ref_src=twsrc%5Etfw">#generative</a> <a href="https://twitter.com/hashtag/art?src=hash&amp;ref_src=twsrc%5Etfw">#art</a> <a href="https://twitter.com/hashtag/digital?src=hash&amp;ref_src=twsrc%5Etfw">#digital</a> <a href="https://twitter.com/hashtag/creativecoding?src=hash&amp;ref_src=twsrc%5Etfw">#creativecoding</a> <a href="https://twitter.com/hashtag/VJ?src=hash&amp;ref_src=twsrc%5Etfw">#VJ</a> <a href="https://twitter.com/hashtag/Processing?src=hash&amp;ref_src=twsrc%5Etfw">#Processing</a> <a href="https://twitter.com/hashtag/generativeart?src=hash&amp;ref_src=twsrc%5Etfw">#generativeart</a> <a href="https://twitter.com/hashtag/livecoding?src=hash&amp;ref_src=twsrc%5Etfw">#livecoding</a> <a href="https://t.co/RkKiuxh5X3">pic.twitter.com/RkKiuxh5X3</a></p>&mdash; 水落 大 mizumasa (@_mizumasa) <a href="https://twitter.com/_mizumasa/status/1030646947395162112?ref_src=twsrc%5Etfw">2018年8月18日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# まとめ

Processing上で実行可能な数式(シェーダープログラム)のガチャをつくりました。
最初にPythonモードを入れる以外は依存ライブラリもないので是非お試しくださいー。