SmoothQueue-IFSの解説


# 要約

  • スムーズに変化するIFSを考えました
  • IFSに用いる関数をFIFOの固定長Queueに入れる感じにできます
  • Enqueue/Dequeueをスムーズにでき、イージングをかけることもできます

# はじめに

2023-06-10にVRChatで行われたlambda (opens new window)というDJ/VJイベントでVJをやらせて頂きました。

この記事では、VJシステムをシェーダーで書くに当たって使用した一部の技術(考え方?)を解説します。

なお、VJ全体についての記事は後々書くと思いますので、そちらについてはあまり触れません。

# 目標

私がVJをすることが決まって一番最初に「やりたいな~」と思った作品があります。

左下のTESTを見ると分かりやすいのですが、UVがバキバキに変形しています。
これを見て、UVをバキバキにしたいと強く思いました。

バキバキにする方法ですが、私の意思はあまり介在してほしくないのでIFS (opens new window)を使う事にしました。

普通にIFSするだけだとビートに合わせたスムーズなIFSの変形が出来ないため、スムーズに変化するIFSを考えました。私は勝手にこれを『SmoothQueue-IFS』と呼んでいます。
(既に呼び方があったらすいません)

# 解説

SmoothQueue-IFSをUVに対して行う簡単な例を示します。

/*
省略しているFuncでは、与えられたシードによって以下の二つの関数を選択するようになっています
- F0 : UVをランダムに平行移動する
- F1 : UVをランダムに回転する

Twiglで動くコードの全文はこちらです
https://twigl.app?ol=true&ss=-NXkgvDINPBSTVu3hvBR
*/

// uvを[-1, 1]の範囲に変換
vec2 suv = (uv * 2.0 - 1.0);

// Queueを更新する時間間隔
const float span = 0.5;
// Queueの長さ
const int count = 4;
for(int i = 0; i < count; i++)
{
    // i番目の関数に流れる時間の定義
    float lt = time / span + float(i);
    float li = floor(lt);
    // 時間の小数部分
    float lf = fract(lt);
    // ↑にイージングをかける
    lf = pow(smoothstep(0.0, 1.0, lf), 0.3);
    // 時間の整数部分をハッシュにかけて、関数に固有なシードとする
    vec3 s3 = hash31(li) * 42.0;

    // Enqueue/Dequeueをスムーズにする
    float enque = lf;
    float deque = 1.0 - lf;
    // 添え字によって関数の強さをスムーズに変えることでEnqueue/Dequeueをする
    float ins = ((i == 0) ? deque : (i == count - 1) ? enque : 1.0);

    // uvに関数を適用
    suv = Func(suv, s3, ins);
}
// 変換したuvを[0, 1]の範囲に戻す
uv = (suv + 1.0) * 0.5;

SmoothQueue-IFSでは、使用する関数は以下の形で定義します。

/*
target : 関数を適用する対象
seed   : 関数に固有なシード
ins    : 関数の強さ (0.0,1.0)で、0.0の時に関数は何もしない
*/
Hoge Func(Hoge target, vec3 seed, float ins)
{
    // targetに対する操作
}

今回の例だと、UVの平行移動は以下の様に定義できます

// UV移動
vec2 F0(vec2 uv, vec3 s3, float ins)
{
    float len = 0.5 * ins;
    vec2 offset = (hash23(s3) - 0.5) * len;
    return uv + offset;
}

以上です。
また、私は言葉による解説が苦手なので、関数の適応をするQueueを可視化してみました。

# 結果

lambdaでのVJで使用した例を紹介します。

# Glitch

こちらは以下の関数が使われています

  1. uvのfold(折り畳み)
  2. uvの回転

# Window

こちらは以下の関数が使われています

  1. uvに対してウィンドウの形状を指定し、固有の識別子を与える

# まとめ

今回はuvに対するIFSのみになってしまいましたが、他の情報に対するIFSもおもしろそうだと思いました。
SDFにSmoothQueue-IFSを適用するとおもしろいものが出来そうですね