DOT NOTES

Twitter:@dot_not_ GitHub:@ogrew

自分の好きなGlitchを考える。

glslスクールでせっかくグリッチを教わったのでその話です。
スクールで教わったグリッチはモザイク化(ブロックで分割して解像度を下げる)してからランダム値を返す関数を使ってテクスチャをずらしてグリッチをかけるというものでした。

まず横方向のみだったサンプルを拡張して縦横両方向にずれるようにします。

out vec4 fragColor;
uniform vec2 resolution;
uniform float time;
uniform float threshold;
uniform vec2 blockSize;

float rnd(vec2 n){
    float a = 0.129898;
    float b = 0.78233;
    float c = 437.585453;
    float dt= dot(n ,vec2(a, b));
    float sn= mod(dt, 3.14);
    return fract(sin(sn) * c);
}

void main()
{
    float blockV = blockSize[0];
    float blockH = blockSize[1];
    float Vert = floor((vUV.t) / blockV) * blockV;
    float Horz = floor((vUV.s) / blockH) * blockH;

    // 0.0 ~ 1.0のランダムな結果を返す
    float gNoiseV = rnd(vec2(Vert, time*.1));
    float gNoiseH = rnd(vec2(Horz, time*.2));
    // thresholdを基準に0 or 1を返す
    float gStepV = step(gNoiseV, threshold * 2.);
    float gStepH = step(gNoiseH, threshold);
    // ズレの強度を決める
    float gStrengthV = gNoiseV / threshold;
    float gStrengthH = gNoiseH / threshold;
    // -1.0 ~ 1.0の間に補正する
    gStrengthV = gStrengthV * 2.0 - 1.0;
    gStrengthH = gStrengthH * 2.0 - 1.0;

    float V = gStepV * gStrengthV;
    float H = gStepH * gStrengthH;

    vec2 coord = vUV.st + vec2(V + H, 0.0);
    vec4 color = texture(sTD2DInputs[0], coord);

    fragColor = TDOutputSwizzle(vec4(color.rgb, 1.0));
}

ex1)

threshold -> 0.1
blockSize -> [0.6, 1.0]

f:id:taiga006:20191111154628g:plain

ex2)

threshold -> 0.3
blockSize -> [0.4, 0.02]

f:id:taiga006:20191111155037g:plain

パラメータでだいぶ結果が変わります。いい感じです。
ただもう少し元絵に動きを加えて不安な感じ?にしたいなぁと思いました。
参考文献を見て三角関数でテクスチャにオフセットを足すといい感じになりました。

out vec4 fragColor;
uniform vec2 resolution;
uniform float time;
uniform float threshold;
uniform vec2 blockSize;
uniform vec2 length;
uniform vec2 width;
uniform vec2 speed;

float rnd(vec2 n){
    (...省略...)
}
// Offsetといっても結局ただの時間依存のsin波を返すだけ
float calcOffset(float pos, float length, float width, float speed) {
    return sin(pos * length + mod(time, 3.0) * speed) * width;
}

void main()
{
    float blockV = blockSize[0];
    float blockH = blockSize[1];
    (...省略...)
    float V = gStepV * gStrengthV;
    float H = gStepH * gStrengthH;

    float offsetX = calcOffset(vUV.s, length[0],width[0],speed[0]);
    float offsetY = calcOffset(vUV.t, length[1],width[1],speed[1]);

    vec2 uv = vec2(vUV.s + offsetX, vUV.t + offsetY);

    vec2 coord = uv + vec2(V + H, 0.0);
    vec4 color = texture(sTD2DInputs[0], coord);

    fragColor = TDOutputSwizzle(vec4(color.rgb, 1.0));
}

ex3)

threshold -> 0.05
blockSize -> [0.5, 0.05]
length -> [12, 8]
width -> [0.003, 0.005]
speed -> [32,18]

f:id:taiga006:20191111202818g:plain

参考

nogson2.hatenablog.com

relbboxってなんやねん、という話。

つまりは

ジオメトリの境界ボックスを基準に、指定したポイントの相対位置を返します。

ということらしい。基本的には

vector  relbbox(<geometry>geometry, vector position)

この構文。

geometryは何番目のインプット?のgeometryかって指定だから最初のうちは0とかせいぜい1しか使わない。そのgeometry内でのpositionの相対位置(つまり0~1)を返す。 ちなみにgeometryの指定は飛ばすことができるみたいで

vector  relbbox(vector position)

って書くこともできる。(ただしこの形式は今後消される可能性あり。)

ちなみにExpression記法?として

$BBX = relbbox(@P).x
$BBY = relbbox(@P).y
$BBZ = relbbox(@P).z

と同義らしい。

ex1) Y座標の相対的な大きさに比例してX座標をずらす。(→加算)

@P.x = @P.x + relbbox(0, @P).y * ch("strength");

f:id:taiga006:20191106222418p:plain

ex2) Y方向の相対的な大きさに比例してXZが広げる/細める。(→乗算)

@P.x = @P.x * pow(ch("strength"), relbbox(0, @P).y);
@P.z = @P.z * pow(ch("strength"), relbbox(0, @P).y);

f:id:taiga006:20191106222932p:plain ※strengthの値を1以下にすれば先端はとがる。

ex3) Y座標の相対的な大きさに比例してXY平面を曲げる。

float bby = relbbox(@P).y;
@P.x = @P.x * cos(radians(bby * ch("strength"))) - @P.y * sin(radians(bby * ch("strength")));
@P.y = @P.x * sin(radians(bby * ch("strength"))) + @P.y * cos(radians(bby * ch("strength")));

f:id:taiga006:20191106224300p:plain

ただ結局
relative + box ?
rel +b? + box ?
わからないままである。

「デザインのデザイン」を読んだ。

f:id:taiga006:20191106182252j:plain

原研哉著「デザインのデザイン」を読んだ。

GLSLスクールの時間までの空き時間に偶然立ち寄った日本橋丸善で買った。前から名前だけは聞いたことのあった本で、表紙のシンプルさに目を惹かれパラパラ読み飛ばしているうちにレジに並んでいた。最近は専らエッセイ集ばかりを読みふけっていたので新鮮な気持ちで読めた。

最初のデザインの歴史を読み解く部分は門外漢の自分にはやや難解であったが、適宜、線を引いたり調べたりしながら読むことでなんとか理解することができた。そういえば本に線を引いたのも久しぶりのことだった。

語り尽くされたであろう「アート」と「デザイン」の違いについてもしっくりいく説明がなされていた。前段の歴史の部分はこの説明のためにまとめられたと言っていいかもしれない。

無印良品のコンセプトを改めて説明するくだりが印象深い。

(「これがいい」ではなく「これでいい」と言う満足感を目指すという説明の後。)消費社会も個別文化も「が」で走ってきて世界の壁に突き当たっている。そう言う意味で、僕らは今日「で」の中に働いている「抑制」や「譲歩」、そして「一歩引いた理性」を評価すべきである。「で」は「が」よりも一歩高度な自由の携帯ではないだろうか。(中略)明瞭で自信に満ちた「これでいい」を実現すること、それが無印良品のヴィジョンである。

この本はタイトルからいかにも意匠を志す者らに向けられた書籍のように見受けられるが、実際はあとがきにもある通りデザインに疎い自分のような人間がデザインのその繊細さ・掴みどころの無さに触れ、実際にその言葉に耳をすます人たちとの交感のきっかけになることを目指した本であった。

久しぶりに無地良品のカレーが食べたくなったのでオンラインショップを見てみるとオンラインショップサイトのweb designはあまり洗練されていないように感じた。靴下とグリーンカレーをまとめ買いした。

デザインのデザイン

デザインのデザイン

hsv2rgbを”完全に理解した”話。

最近にしてはTwitterの伸びが良かった投稿。 glslfan,glslsandbox等を見たものにglslスクールで学んだテクを織り込んだだけのものです。

その中でフラグメントシェーダーを扱うとき色の扱いが難しいと感じました。 というのも、一番最初に学んだ色の表現方法が vec3(r,g,b) の形式でこれだと求めていた色合いを表現するのに非常にあくせくします。

※以下のshaderはTouchDesigner上のGLSL TOPを利用しています。

uniform vec2 resolution;
uniform vec3 color;
uniform float time;

out vec4 fragColor;

void main() {
    vec2 st = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);

    float len = length(st);    
    float freq = 20.;
    float speed =2.;    
    float pattern = abs( sin(len * freq - time * speed) );

    float light = .5;
    pattern = light / pattern;
    
    vec3 color = vec3(color.x, color.y, color.z);
        
    vec4 colorOut = vec4(color * pattern, 1.0);
    fragColor = TDOutputSwizzle(colorOut);
}

f:id:taiga006:20191029235047g:plain

※resolution, color, timeはそれぞれGLSL TOPの設定でよしななものを入れています。

上ではred, blueをそれぞれ動かしていますが、色の変化がスムーズには感じられません。 そこでProcessing Community Day Tokyo 2019での一幕を思い出すのです。 そうです、HSV(HSB)を使います。

uniform vec2 resolution;
uniform vec2 params;
uniform float time;

out vec4 fragColor;

vec3 hsv(float h, float s, float v){
    vec4 t = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(vec3(h) + t.xyz) * 6.0 - vec3(t.w));
    return v * mix(vec3(t.x), clamp(p - vec3(t.x), 0.0, 1.0), s);
}

void main() {
    vec2 st = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);

    float len = length(st);    
    float freq = 20.;
    float speed =2.;    
    float pattern = abs( sin(len * freq - time * speed) );

    float light = .5;
    pattern = light / pattern;
    
    float h = abs(sin(time)); 
    float s = params.x; // saturation
    float v = params.y; // value
    vec3 color = hsv(h, s, v);        
        
    vec4 colorOut = vec4(color * pattern, 1.0);
    fragColor = TDOutputSwizzle(colorOut);
}

f:id:taiga006:20191029235155g:plain

※resolution, params, timeはそれぞれGLSL TOPの設定でよしななものを入れています。
※params.xy はそれぞれsaturation, value(brightness)に対応しています。

ここで注目すべきはhsv関数の部分です。

vec3 hsv(float h, float s, float v){
    vec4 t = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(vec3(h) + t.xyz) * 6.0 - vec3(t.w));
    return v * mix(vec3(t.x), clamp(p - vec3(t.x), 0.0, 1.0), s);
}

もちろん、この関数は自分で思いついたものではありません。(自力でこれは無理やー!)

以下の記事などに乗っている実装を参考に(というかコピペ)しています。

www.laurivan.com nogson2.hatenablog.com qiita.com

ではこの実質3行の関数の中では何が行われているのでしょうか?

マジックナンバーが多すぎて何がなんだかです。

検索するとwikipediaなどに詳しく書いてありました。

とりあえず読んでみます。

ja.wikipedia.org

hsvはよく円錐、円柱のモデルでわかりやすく説明されます。(それは知ってた。)

f:id:taiga006:20191029221239j:plainWikiより)

見て分かる通り、3D空間上の単位円の円周上をH(Hue)、円の半径をS(Saturation)、そして円柱の高さ(深さ?)をV(Value)としてそれぞれ見立てることができます。 なるほど?

ここでS=0であればr=g=b=Vとなることがわかります。(つまりグレースケールです。)

その後、急に場合分けの式が出てきてわけがわからないので他のサイトを見ていきます。

これらのサイトを読みました。

www13.plala.or.jp ofo.jp www.peko-step.com

だんだんわかってきました。 つまるところ、こんな感じです。

f:id:taiga006:20191029224802p:plainRGB←→HSB相互変換【Windowsプログラミング研究所】さんより)

上の記事にもある通り、注目すべきはそのHSVの最大・最小値です。 その最大・最小値はHueの値によって計算方法が変わります。それは上の円を見てもらえればわかると思います。円をぐるっと回っていく中で増減しているRGBの対象がかわっていくのがわかります。それが具体的には60度ずつで変わっていきます。これに対応する形で最大・最小値も変わっていくことになります。このパターン分けがあるせいで計算が難しくなっています。

さてこのへんで座学は飽きてきました。というか7割位理解できた気がします。この「60度ごと」鍵となっていて最初の関数の t.xyz が出ていることがわかってきたのであとは愚直なコードを書いてみて比較することにします。

できました。

vec3 hsv(float h, float s, float v){
    float r = v, g = v, b = v;

    if (s == 0) {
        return vec3(r,g,b);
    }

    h *= 6.; // 0.0 ~ 1.0スケールのものを360を6分割した60度ごとに場合分けする

    float i = floor(h); // 60度刻みのどこに属するか
    float f = h - i; // その範囲での割合

    if (i == 0) { // 0 ~ 60度
     // r = v; (MAX)
        g *= 1 - s * (1 - f);
        b *= 1 - s;
    }
    else if (i == 1) { // 60 ~ 120度
        r *= 1 - s * f;
     // g = v; (MAX)
        b *= 1 - s;
    }
    else if (i == 2) { // 120 ~ 180度
        r *= 1 - s;
     // g = v; (MAX)
        b *= 1 - s * (1 - f);
    }
    else if (i == 3) { // 180 ~ 240度
        r *= 1 - s;
        g *= 1 - s * f;
     // b = v; (MAX)
    }
    else if (i == 4) { // 210 ~ 300度
        r *= 1 - s * (1- f);
        g *= 1 - s;
     // b = v; (MAX)
    }
    else if (i == 5) { // 300 ~ 360度
     // r = v; (MAX)
        g *= 1 - s;
        b *= 1 - s * f;
    }
    return vec3(r,g,b);
}

これを使った結果と先程の便利関数を使った結果を比較してみます。

f:id:taiga006:20191029235918g:plain

(左:便利関数、右:愚直関数)

同じ結果と言って差し支えないと思います。

完全に理解したわけではないですが「完全に理解した」レベルにはなったので今回はこれで良しとします。

Houdini勉強メモ(UHTK編)

非常に初学者にも親切でわかりやすい解説でした。メモを晒しておく。


UHTK 04 Part I - VEXで置き換える

www.youtube.com

📝MEMO

  • Wrangleノードはいくつか種類があるが基本的にはAttribute Wrangleをベースにパラメータが適宜変更されているだけ。
  • 存在しないattributeを書くと勝手に追加される。(= attribute createノードと一緒。)
  • @weight = 2.0;(float)v@weight=2.0;(vector)みたいな感じで型を指定できる。
    -@Pv@Pを省略している。
  • VEXはC,C++ベース。
  • pointを追加してリサージュ図形をつくる。
for (float i = 0; i < 2 * 3.141592 * arg; i += 0.02) {
    pos = set(cos(i * f1), sin(i * f2), cos(i * f3)) ;
    addpoint(0, pos);
}
  • pointをつなげるPolyWireノードをつなぐ。

🐔GIF

f:id:taiga006:20191021220308g:plain

UHTK 04 Part II - VEXで置き換える

www.youtube.com

📝MEMO

  • rand関数を使って rand(@primnum) と書くと各プリミティブのIDをシード値にしてランダムな値を作れる。
  • original perlin noiseはvexでonoise(pos, turbulence, rough, atten)関数。
  • chfのようにchrampもある。
  • onoiseはfreq(原点を中心にScale)->offset計算。その逆ではない。

今回書いたVEX

vector d_noise = v@P * chf("d_freq") - chv("d_offset");
vector d_pos = v@P + onoise(d_noise, 2, 0.5, 1.0);

vector pos = d_pos * chf("freq") - chv("offset");
float noise = onoise(pos, 5, 0.5, 1.0);

noise = fit(noise, -0.5, 0.5, 0.0, 1.0);

noise = chramp("remap_height", noise);

float height = chf("height");

vector disp = v@N * noise * height;
v@P =  v@P + disp;

🐔GIF

f:id:taiga006:20191021221500g:plain

UHTK 05 - ソルバーって何?

www.youtube.com

📝MEMO

  • @Frame = 現在のフレーム番号
  • Solver = 前フレームでの処理結果を使いたい=時間経過の結果を蓄積したいときに使える。
  • scatterノードでpointをまばらに。
  • groupノードでグループ化できるが、所詮そのgroupパラメータもattributeのひとつなのでattribute wrangleで書くことができる。
i@group_food = 1;
  • solverの処理結果はsolverの中にいると機能しない。(バグ?)=>Secene ViewのPinを使用するとよい。
  • Cleanノードで、どこともつながっていない(不必要な)pointを掃除してくれる。

🐔GIF

f:id:taiga006:20191021221952g:plain

UHTK 06 Part I - タイトルグラフィックを作る

www.youtube.com

📝MEMO

  • 拡散するようなpointの動きを作るために各pointごとに加速度から位置を決める。
  • 加速度の決定にはカールノイズ curlnoise(@P)を利用する。
float dt = 1./24.;
v@v = v@v + v@a * dt;
v@P = v@P + v@v * dt;
  • 初期値を決める時点でIDを割りふっておく。(なぜ、ptnumを使わないの?→trailでcache文だけpointを複製したとき元ネタとなったptnumでグループ分けできる→lineを作るor色をつけるのに都合がいい。)
  • 徐々に先の太さを変えたい。→あるptがあるlineを構成するptsのうち何番目なのか→resampleノードが使える。→curve U attribute
  • chramp("name", attrs) -> attrsをRampで調整できるようになる。

🐔GIF

f:id:taiga006:20191022002601g:plain

映像制作&シェアで使えるffmpegコマンド置き場(随時更新)

f:id:taiga006:20191020175719g:plain

連番pngからgifを作りたい

paletteの生成

$ ffmpeg -i %d.png -vf palettegen palette.png

gifの作成

$ ffmpeg -f image2 -i %d.png -i palette.png -r 30 -filter_complex paletteuse=dither=bayer:bayer=scale=2 output.gif

動画mp4からgifを作りたい

paletteの生成

$ ffmpeg -i input.mp4 -vf palettegen palette.png

gifの作成

$ ffmpeg -i input.mp4 -i palette.png -r 30 -fileter_complex paletteuse=dither=bayer:bayer=scale=2 output.gif

動画を指定回数分だけ繰り返したい(ループさせたい)

$ ffmpeg -stream_loop 4 -i input.mp4 -c copy output.mp4

終電日記 01。

f:id:taiga006:20190925213854j:plain

誰にも公開しないつもりだったブログの下書きのいくつかを、ベースにした7本。

最近、自分は結構人のエッセイや日記を読むのが好きなんだな、と思うようになりました。 ブログでもなんでも、その人の喜怒哀楽をフィルターなしに見れる気がして。

… osicomagazineを読んだ影響です。

まだ20本くらい中途半端な文章が残っているので、リライトでき次第公開していきます。

ということで毎日23:50にお届けした終電日記、第一弾でした。

重複を除いてレコード数をCOUNTするSQL

あるテーブルの特定のカラムで重複なくカウントしたい場合

SELECT COUNT(*) FROM  (SELECT DISTINCT level FROM player);

みたいにサブクエリとして書いていたが、

SELECT COUNT(DISTINCT level) FROM player;

みたいにかけることを知った。 こちらのほうがスマートだし、わかりやすい。

ただし多くのRDBMSでここのマルチカラムは許容されていない。 その場合は素直にサブクエリにして対応するのが吉。

0924 凡人。

f:id:taiga006:20190923225152j:plain

「安易にBillie Eilishを崇拝しないために、今日も腹に力入れて過ごしてる。」

TLに流れてきたこのフレーズが妙に面白くて、そして心に刺さった。

久しぶりにTLに姿を現した九年来の友人だった。

九年前、僕らはブログ仲間(と言えばいいのだろうか)だった。

当時僕は高二で、彼女は中三(いや、高一だったかもしれない)だった。

お互い日々のなんでもないブログにコメントしあって、連携するチャットサービスでときたま雑談をする中だった。

あるとき僕と彼女はエレファントカシマシのアルバムで何が一番好きか議論した。

僕らは音楽が好きだった。

僕は「ココロに花を」、彼女は「生活」を選んだ。

聞く人が聞けばこの時点で失笑かもしれない。

「生活」はエレカシのアルバムの中でも笑ってしまうほど暗黒期の作品であり、エピックのころの名盤だ。

一方の「ココロに花を」はそんな卑屈なほど内向的だったエレカシが外界に踏み出す一歩となった、言ってしまえば所謂今のエレカシらしさが存分に感じられる聴きやすいアルバムだ。ポニーキャニオン一発目のものだ。

当時の僕としては「生活」はただただ血生臭くて怖いアルバムだった。 でも社会人になった今、間違いなく一番聴いているアルバムは「生活」だったりする。

7曲しかないのに、まるでフルアルバムを聞いているみたいな重量感だ。(実際1曲が5,6分ある。)

そんなどうでもいいことを思い出した。

「あの頃のことを覚えてる?」とメンションする代わりに、僕は彼女のツイートにいいねを押した。

ところで僕はBillie Eilish好きだとか、

全部通じてくれればいいのだけれど。

町に出、笑い、凡人
ああここに有り

凡人-散歩き- - エレファントカシマシ