DOT NOTES

Twitter:@dot_not_ GitHub:@ogrew

面白法人やめた。

Before: 面白法人カヤック  
After: kayac.akiba.studio  

Q.「え、結局カヤックやん。」
A.「まあ子会社ですね☆」

Q.「やめたってか出向…的な?」
A.「在籍出向ではなく転籍出向なので社会的にはこれ転職かなと。雇用形態も変わるので。あともう🎲とか振らないっす、多分。」

Q.「何するの?」
A.「今までゲームのサーバサイド業務がメインでPerlやGoを書いてきましたが今後は映像演出やXR系コンテンツの方にコミットしていきたいゾ。」

Q.「どれくらい面白法人いたの?」
A. 「大学院飛び出して新卒で入ってからだいたい2年半くらい。」

Q.「給料は?」
A.「上がらないですね〜、ついでにボーナスもほぼないのでより懐が寂しくなります。」

Q.「じゃあ、なんでやめたの?」
A.「それは、カクカクシカジカ…直接あったときに聞いてください😥」

Q.「お前の好きな社食行けないじゃん。」
A. 「なんとかカヤックの月イチイベント”つくっていいとも”参加と社員食堂に行っていい権利は死守しました。なのでそのペースで結局鎌倉には行くっていうね…。」

Q.「誕生日おめでとう🎉🎉🎉」
A.「ありがとう〜、正しくは、あと20日で26歳ですね。」

Q.「副業は?」
A.「いろいろルールはあるみたいですが、基本、会社と競業みたいなケースでなければ(個人で請け負える程度の仕事であれば)積極的に受けていきたいですね。会社にも確認しました。」

Q.「来年の目標は?」
A.「もっとたくさんつくるよ。なんてったって”つくる人を増やす”って会社にいたからね。それから引っ越しをする。お金貯めないと。」

Q.「ラジオの仕事じゃないんだ。」
A.「したいねー、ラジオ高崎で帯番組持ってあの高崎駅のオープンスタジオに座るのが夢だからなぁ。とりあえず #350can 頑張るよ。」

ということでこれからもどうぞ、みなさんよろしく。
気が向いたら上の「カクカクシカジカ」の部分も年末に書くかも?
2020年、仕事くれ、、、ください、、、な、なんでもします🍭🍭🍭


いつもの。

#nodevember2019 を振り返る。

この記事はBlender Advent Calendar 2019の3日目の記事です。


先週末、Motion Plus Design Tokyo 2019に行ってきました。

ASH THORPが"Be Prolific"って言ってましたね。2020年もたくさん作っていきましょう!

さて、今年ぬるっと初参加した #nodevember2019 の作品を振り返っていきたいと思います。 (作成したノードもスクショでシェアします。) さまざまなアーティストが公開している作品や動画を参考にしている(当初まとめて公開する予定もなかった)ので申し訳ありませんがリファレンスは今回まとめていません。 気になる方は上のハッシュタグでぜひ検索してみてください。

What is #Nodevember?

f:id:taiga006:20191130211744p:plain:w500

(↑ この間のカヤックの「つくっていいとも」で使った発表資料。) 要は11月のお祭りですね!

それでは早速見ていきましょう。

11/8 Coral

f:id:taiga006:20191130213038j:plain:w500

Node

f:id:taiga006:20191130223306p:plain:w500

11/9 Microbe

f:id:taiga006:20191130211947j:plain:w500

Node

f:id:taiga006:20191130222922p:plain:w500

11/19 Circuit

f:id:taiga006:20191130222516p:plain:w500

Node

f:id:taiga006:20191130222326p:plain:w500

11/19 Circuit(2)

f:id:taiga006:20191130222411p:plain:w500

Node

f:id:taiga006:20191130222212p:plain:w500

11/22 Star

f:id:taiga006:20191130212027j:plain:w500

Node

f:id:taiga006:20191130223840p:plain:w500

11/22 Star(2)

f:id:taiga006:20191130224138j:plain:w500

Node

f:id:taiga006:20191130224058p:plain:w500

11/24 Nebula

f:id:taiga006:20191130211922j:plain:w500

Node

f:id:taiga006:20191130220514p:plain:w500

もう一個くらいやっておきたかったかな。


とてもたくさんの作品が1ヶ月の間で作成されシェアされ盛り上がりました。 私がファンになったアーティストたちを紹介します。

TouchDesignerで画像処理シェーダー。その2

f:id:taiga006:20191124163637j:plain さっそく第二弾。

今回は、先日無事終わったGLSLスクールで学んだものとよく使うテクスチャいじりのものを載せておきます。

★RGBずらし

f:id:taiga006:20191124023459j:plain

// RGB shift
uniform vec2 offset;

out vec4 fragColor;

void main()
{
    vec2 coord = vUV.st;
    vec4 color = texture(sTD2DInputs[0], coord);

    vec4 negColor = texture(sTD2DInputs[0], coord + vec2(-offset[0], 0.0));
    vec4 posColor = texture(sTD2DInputs[0], coord + vec2(offset[1], 0.0));

    fragColor = TDOutputSwizzle(vec4(negColor.r, color.g, posColor.b, color.a));
}

ちなみに色収差とRGBずらしは正しくは同じものではないとのこと。(同じだと思ってた。)

★クランプ

f:id:taiga006:20191124023442j:plain

// clamp
uniform float clampMin;
uniform float clampMax;

out vec4 fragColor;

void main()
{
    vec2 coord = clamp(vUV.st, clampMin, clampMax);
    vec4 color = texture(sTD2DInputs[0], coord);

    fragColor = TDOutputSwizzle(color);
}

clamp関数はclamp(x, min, max)で最大最小を制御するものです。

★拡縮

f:id:taiga006:20191124142441j:plain

// scaling
out vec4 fragColor;

void main()
{
    vec2 newUV = 6.0 * vUV.st;
    vec4 color = texture(sTD2DInputs[0], newUV);

    fragColor = TDOutputSwizzle(color);
}

基本的なやつです。よく使います。ここではInput Extend Mode UVをMirrorに変えています。

★テクスチャゆがみ

f:id:taiga006:20191124154234j:plain

// distortion
uniform float scale;
uniform vec2 center;

out vec4 fragColor;
void main()
{
    vec2 direction = vUV.st - center;
    vec2 coord = vUV.st + scale * normalize(direction);
    vec4 color = texture(sTD2DInputs[0], coord);

    fragColor = TDOutputSwizzle(color);
}

centerからの方向に対してスケールさせることでゆがみを演出しています。scaleの正負を変えるとだいぶ印象が変わります。

★溶解

f:id:taiga006:20191124160227j:plain

// melt
uniform float time;

out vec4 fragColor;

void main()
{
    vec2 coord = vUV.st;
    
    vec2 melt = vec2(coord.x, coord.y*0.15+time*0.1);
    vec3 meltTex = texture(sTD2DInputs[0],melt).rgb;
    meltTex = melttex / 10.0;
    coord = coord.xy - meltTex.xy;
    vec4 color = texture(sTD2DInputs[0], coord);
    
    fragColor =TDOutputSwizzle(color);
}

1つのテクスチャだけでやっていますが別の画像やノイズをInput[1]に入れて使うともっと面白くなります。

へこみ

f:id:taiga006:20191124163519j:plain

// dent
uniform vec2 width;
uniform vec2 center;

out vec4 fragColor;
void main()
{
    vec2 coord = vec2(2.0) * vUV.st - vec2(1.0); // -1 ~ +1

    vec2 s = sign(coord);
    coord = abs(coord);
    coord = coord * (1. + smoothstep(width.x, width.y, coord + center));
    coord = 0.5 * s * coord;
    coord = coord / 2.0 + 0.5;

    fragColor = texture(sTD2DInputs[0],coord);
}

Paletteの中にあるdentに少し手を加えたものです。これもよく使いますね。

TouchDesignerで画像処理シェーダー。その1

f:id:taiga006:20191121133229j:plain

シリーズ化を宣言することで続けていかなければならないカルマを背負う。

今日はICSさんの記事にあったWebGLの簡単な画像処理をTouchDesignerでも使えるGLSLに。

★モノクロ

f:id:taiga006:20191121133817j:plain

// monochrome

out vec4 fragColor;

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);
    float gray = dot(color.rgb, vec3(1.0/ 3.0));
    color = vec4(vec3(gray), 1.0);

    fragColor = TDOutputSwizzle(color);
}

(r,g,b)とvec3(1/3)の内積を取ることが平均になるところがポイント。

★ネガポジ反転

f:id:taiga006:20191121133352j:plain

// reversal

out vec4 fragColor;

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);

    vec3 reversalColor = vec3(1) - color.rgb;
    color = vec4(reversalColor,1.);

    fragColor = TDOutputSwizzle(color);
}

シェーダーで使われるrgbは0.0~1.0なので反転させるには1.0から引けば十分です。

★モザイク

f:id:taiga006:20191121133555j:plain

// mosaic

uniform vec2 res;
uniform float mosaicScale;

out vec4 fragColor;

void main()
{
    vec2 mUV = floor(vUV.st * res / mosaicScale)  / (res/ mosaicScale) + (mosaicScale/2.) / res;
    vec4 color = texture(sTD2DInputs[0], mUV);

    fragColor = TDOutputSwizzle(color);
}

強引に1行にまとめました。 mosaicScaleで縦横分割した何ブロック目にいるのか、そしてそのブロックの中央のピクセルを見て色を決めます。

★すりガラス

f:id:taiga006:20191121133439j:plain

// glass

uniform vec2 res;
uniform float time;

out vec4 fragColor;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);

    float r = 7.;
    float x = (vUV.s * res.x) + rand(vUV.st) * r * 2. - r;
    float y = (vUV.t * res.y) + rand(vUV.ts) * r * 2. - r;

    color = texture(sTD2DInputs[0], vec2(x,y) / res);

    fragColor = TDOutputSwizzle(color);
}

ある一定の距離感覚の範囲の中からランダムでピクセルの色を取得しています。

★2値化

f:id:taiga006:20191121133512j:plain

// threshold

uniform float threshold;

out vec4 fragColor;

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);

    float v = dot(color.rgb, vec3(1.0/3.0));
    if (v >= threshold) {
        v = 1.0;
    } else {
        v = 0.0;
    }

    fragColor = TDOutputSwizzle(vec4(v,v,v,1.0));
}

モノクロの応用です。と言ってもある閾値を設けてそれ以上であれば白、未満なら黒にするだけです。

★ランダムディザー

f:id:taiga006:20191121133634j:plain

// random dithering

uniform float threshold;

out vec4 fragColor;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);

    float v = dot(color.rgb, vec3(1.0/3.0));
    if (v >= rand(vUV.st)) {
        color.rgb = vec3(1.0);
    } else {
        color.rgb = vec3(0.0);
    }

    fragColor = TDOutputSwizzle(color);
}

先程作った2値化の閾値自体をランダムに決めるというものです。


ics.media

HOW TO RENDERを読んだ。

f:id:taiga006:20191120184346j:plain

オフィスが鎌倉から秋葉原になってから会社の本棚というものをよく見るようになった。鎌倉のときは良くも悪くもほとんど事業部ごとに各ジャンルの専門書籍がまとまっていた。一方で秋葉原のオフィスはすべての書籍が一箇所にまとまっており、それこそ最近の漫画から今日紹介するような専門書まで追いてあり背表紙を眺めているだけでも興味深い。

さてスコットロバートソンといえば”HOW TO DRAW”が有名だと思うが(専門外の自分でも聞いたことがあったので)、今回読んだのは”HOW TO RENDER”。まえがきに当たる部分で「この本は”HOW TO DRAW”の続きにあたる」といったことが書かれてる。実はまだ”HOW TO DRAW”を読んだことはないのだけれど(え?)、ざっと立ち読みして面白そうだったので借りて読んでみた。

読んでみて、たしかにこの書籍はアーティスト(特に3Dオブジェクトを2Dで表現する人たち)に向けた専門書の位置づけというのが相応しい内容で具体的には光や影、そして反射といった様々な自然の中の事象ごとに、ドローイングされたものをよりリアリスティックに表現し、それを見る人(主に非アーティスト)に対しての視覚言語の意味合いをより強く裏付ける手法として、実際の物理的な側面から学ぶと言ったものだった。

また、自分のようなエセ3D芸術家ごっこをしている人間でもちょっと学びはあった。特に反射、光沢あたりのパートはいわゆるアーティストと言われる人間でなくても日常の観察眼としてモノを見るときの楽しみ方をより豊かにしてくれるような内容だった。ところどころ翻訳がうまく言っていないようなところが見受けられたがそこまで気にするレベルのものでもなかった。

そして、これは特にアーティスト向けのコンテンツとなるが、この書籍(そしてどうやら”HOW TO DRAW”のほうも)には専用のアプリがありそれをダウンロードすると専用のカメラツールが立ち上がり本書内の各スケッチにそれをかざすとスケッチの様子の解説動画が再生されるようである。

自分は特にやろうとは思わないが、実際に手を動かして学ぶこともできるという意味で非常に教育向けの本であることは間違いないと言える。

さて、次は何を借りてみようかなー。

自分の好きな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

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

www.youtube.com

📝MEMO

  • ツールシェルフからCtrlを押しながらCameraを押すと現在の視点でカメラを作成できる。
  • Ctrl + N でN画面分割ができる
  • ROP Camera = Rendering Operator Camera マントラ側で正しく設定していればこれを使えば良い
  • Render ViewのPreviewをOFFにしてMaterialやライトの調整を行うと良い
  • Pixel Samples を上げてノイズを下げる

🐔FINAL

f:id:taiga006:20191124195858j:plain