つぶやきGLSLで使ってるレイベクトル
つぶやきGLSLでraymarchingをする時に使ってるレイベクトルについて書いてみます。twiglのgeekest(300es)のフォーマットで話は進めます。
通常のレイベクトルは
normalize(vec3(((FC.xy*2.-r)/r.y),2))
と、だいたい2.0
くらいを焦点距離にして作る。それをつぶやきGLSLでは手を抜いて
vec3(((FC.xy-.5*r)/r.y),1)
として運用している。正規化をしてないのだから画面中央から離れれば離れる程ベクトルのスカラーが大きくなるのは想像出来る。raymarchigのロジックからしても破綻してる。なのに解っていても、これを使う理由は一つ。短く書けるからだ。これをそのまま使うと画面の端にはアーティファクトが発生する。さて、どうするか?対処法としてSDFに重みを付ける。
sdf(p)*0.5
みたいな感じ。
一度、絵を出してみる。
通常の場合
normalize(vec3(((FC.xy*2.-.r)/r.y),2))
正規化を外した場合
vec3((FC.xy*2.-r)/r.y,2)
当然ヒドイ事になる。
次に焦点距離とuvの範囲を変えてみる。
vec3((FC.xy-.5*r)/r.y,1)
だいぶマシになった。ここでSDFに重みを付けてみる。この場合のSDFは
(length(p)-.3)*.7
これで使えるレベルまで来た。ちょっとアーティファクトがあるけど気になるなら重みを変えるだけ。
これをすると画面が暗くなるけど、違うところで調整すれば良い。
こんなトリックで成立している。フラクタルをするなら、必ず重みの調整がついてまわるので*.7
は文字数の内に入らない。フラクタルの初期倍率は、1.0
なのだが、それを2.0
にするだけ。
更に美味しい話がある。
通常のraymarchingの進行は。(進んだ距離をg
とした場合)
vec3 rd=normalize(vec3(((FC.xy*2.-r)/r.y),2); vec3 ro=vec3(0,0,-5); .... p=g*rd+ro; ...
つぶやきGLSLで書くなら
p=g*normalize(vec3(((FC.xy*2.-r)/r.y),2); p.z-=5.;
これが、normalize()
を使わないなら
p=vec3((FC.xy-.5*r)/r.y*g,g-5.)
これで済む。これで、かなりの文字数が削減された。つぶやきGLSLで、ここまでは読み切れないと思うので説明してみました。あくまでも、つぶやきGLSLの上のスキルと承知して使ってください。ループの中で毎回レイベクトルを生成してるし、画面中央部分のスッテプ回数は通常より多くなるので、コストパフォーマンスは悪過ぎです。文字数優先の為にこうなったけど本能的に受け付けないかも。自分も最初はGPU酷使には抵抗がありました。
shaderでmat4を使う為のあれこれ
最近はGPUが強くなってきたのでraymarchingで複数のオブジェクトも大丈夫かなと思えるようになってきた。だけど扱うオブジェクトの数が増えてくれば姿勢の管理の大変になる。そこでmat4を使ってみます。 普通raymarchingをするならmat4なんて面倒で使いたくない。最初はそうだったけどスキニングを始めたらmat4が便利でビックリでした。そこで得た方法なんかを書いていこうと思います。ただ、1個2個程度なら使わない方が良いかな。
使い方
mat4は数学どうのこうのというより乗算する順番が全てです。どちらかというと感覚で使った方が良いんじゃないって思えてきます。使っていて変なら掛ける順番を入れ替えるだけ。まあそんな感じで使ってます。使う行列は移動行列と回転行列だけです。skew行列とか拡大行列とかありますが距離関数の特性上アーティファクトが発生するので使えません。かえって分かりやすくて良いです。基本姿勢操作だけと思ってください。
説明が難しいので、雰囲気で書いて順番を入れ替えていけば、そのうちにツボが分かります。ここは確かめてはいませんが使っている感じだとvertex shaderの行列処理の逆行列を使っているみたいです。vertex shaderの行列処理とは反対だと思った方が良さそうです。
使える行列を書きだします。
mat4 move(vec3 p) { return mat4(1,0,0,0,0,1,0,0,0,0,1,0,-p,1); } mat4 rotX(float a) { float s=sin(a),c=cos(a); return mat4(1,0,0,0,0,c,s,0,0,-s,c,0,0,0,0,1); } mat4 rotY(float a) { float s=sin(a),c=cos(a); return mat4(c,0,s,0,0,1,0,0,-s,0,c,0,0,0,0,1); } mat4 rotZ(float a) { float s=sin(a),c=cos(a); return mat4(c,s,0,0,-s,c,0,0,0,0,1,0,0,0,0,1); } mat4 rot(vec3 axis, float t) { vec3 a = normalize(axis); float s=sin(t),c=cos(t),r=1.0-c; return mat4( a.x*a.x*r+c,a.y*a.x*r+a.z*s,a.z*a.x*r-a.y*s,0, a.x*a.y*r-a.z*s,a.y*a.y*r+c,a.z*a.y*r+a.x*s,0, a.x*a.z*r+a.y*s,a.y*a.z*r-a.x*s,a.z*a.z*r+c,0, 0,0,0,1); } mat4 pointAtX(vec3 d) { vec3 s = normalize(cross(d,vec3(0,1,0))); return inverse(mat4(d,0,cross(s,d),0,s,0,0,0,0,1)); } mat4 pointAtY(vec3 d) { vec3 s = normalize(cross(d,vec3(0,0,1))); return inverse(mat4(s,0,d,0,cross(s,d),0,0,0,0,1)); } mat4 pointAtZ(vec3 d) { vec3 s = normalize(cross(d,vec3(0,1,0))); return inverse(mat4(s,0,cross(s,d),0,d,0,0,0,0,1)); }
moveは移動行列(引数は移動ベクトル)
rotX,rotY,rotZは各軸ごとの回転行列(引数は回転角)
rotは任意軸の回転行列(引数は回転軸、回転角)
pointAtX,pointAtY,pointAtZは各軸を任意方向と一致させる行列。これについては私がつくったので怪しいところがあったら適宜直してください。これには補助ベクトルが必要ですが関数の中に組み込んじゃってます。これにはinserveを使っているのでes300でしか使えません。inserveでなくてもtransposeでもいいみたいですがWebGL1.0では使えませんでした。WebGL1.0で使いたいならmat4の中身をtransposeして書き換えてください。これはジンバルロックを起こす可能性があります。補助ベクトルと任意ベクトルの一致は避けてください。
これらをこんな感じで使ってます。
mat4 m=mat4(1); m*=rot(normalize(vec3(1,2,3)),time); m*=move(vec3(1,2,3)); m*=rotZ(time*0.2); m*=rotX(time*0.3); p=(m*vec4(p,1)).xyz;
サンプル
最後に
色々なパターンの行列処理を考えましたが使って覚えるしかないかなって思ってしまいました。グループで行列を作って置いて、それに移動行列を乗算して使ったりとか色々できると思います。個々に回転させてベクトルを足して移動とかしなくていいので管理は楽になると思います。任意軸の回転行列も使えるのでクォターニオンじゃなくてもmat4だけで済んでしまうのもいいと思います。
ChucKにmusic shaderを移植
ChucKにmusic shaderを移植してみた。 まだ覚え始めなので、怪しいところもあると思います。
今回使ってるadsrの関数には、バグがあるので鵜呑みにしないでください。ここでは正常に動いているけど、違うパターンでダメな時がありました。そのうち直します。
元のmusic shaderはこれ。
#define BPM 140. #define A (15./BPM) float adsr(float t, vec4 e, float s) { return max(0.0, min(1.0, t/max(0.0001, e.x)) - min((1.0 - s) ,max(0.0, t - e.x)*(1.0 - s)/max(0.0001, e.y)) - max(0.0, t - e.z)*s/max(0.0001, e.w)); } float square(float f) { return sign(fract(f)-0.5); } float kick(float t){ return cos(315.0 * t - 10. * exp( -50. * t )+0.3)*adsr(t,vec4(0.0, 0.3, 0.0, 0.0), 0.0); +0.2*square(50.*t)* adsr(t,vec4(0.0, 0.05, 0.0, 0.0), 0.0); } float snare(float t) { return square(3063.0*t*sin(t*8000.0)) * adsr(t,vec4(0.005, 0.08, 0.0, 0.0), 0.0); } float closeHihat(float t) { return square(2763.0*t*sin(t*8500.0)) * adsr(t,vec4(0.0, 0.03, 0.0, 0.0), 0.0); } float openHihat(float t) { return square(2763.0*t*sin(t*8300.0)) * adsr(t,vec4(0.0, 0.05, 0.03, 0.03), 0.5); } float sequence(int s,float t) { float n =mod(t,A); for(int i=0;i<16;i++){ if((s>>(int(t/A)-i)%16&1)==1)break; n+=A; } return n; } vec2 mainSound( float time ) { int i = int(floor(time/(A*16.)))&3; int velocity = int[](0x3030,0x3030,0x3030,0x3030)[i]>>(int(floor(time/A))&15)&1; float vol = 0.2 *(1.0+1.5*float(velocity)); return vec2( 0.0 + 0.3 * kick(sequence( int[](0x0c05,0x0c05,0x0c05,0x0c0c)[i],time)) + 0.3 * snare(sequence( int[](0x9290,0x9290,0x4290,0x4292)[i],time)) + vol * closeHihat(sequence(int[](0x5555,0x5555,0x5555,0x5155)[i],time)) + vol * openHihat(sequence( int[](0x0000,0x0000,0x0000,0x0400)[i],time)) ); }
Chuckでサンプリング音源を使った奴。
140 => float bpm; 1::second * 15 / bpm=> dur tick; fun void kick(){ SndBuf buf => Gain g => dac; .25 => g.gain; me.dir() + "data/kick.wav" => buf.read; [0x0c05,0x0c05,0x0c05,0x0c0c]@=> int seq[]; 0 => int bar; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => buf.pos;} 1::tick => now; } bar++; } } fun void snare(){ SndBuf buf => Gain g => dac; .4 => g.gain; me.dir() + "data/snare.wav" => buf.read; [0x9290,0x9290,0x4290,0x4292]@=> int seq[]; 0 => int bar; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => buf.pos;} 1::tick => now; } bar++; } } fun void hihat(){ SndBuf buf => Gain g => dac; .25 => g.gain; 0 => int bar; [0x5555,0x5555,0x5555,0x5155]@=> int seq[]; me.dir() + "data/hihat.wav" => buf.read; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => buf.pos;} 1::tick => now; } <<<"bar",bar>>>; bar++; } } fun void openHihat(){ SndBuf buf => Gain g => dac; .25 => g.gain; 0 => int bar; [0x0000,0x0000,0x0000,0x0400]@=> int seq[]; me.dir() + "data/hihat-open.wav" => buf.read; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => buf.pos;} 1::tick => now; } bar++; } } spork ~ kick(); spork ~ snare(); spork ~ hihat(); spork ~ openHihat(); 3::minute=> now;
ChucKで音源をmusic shaderに合わせた奴。
音源のclassをつくるのに参考にしたページ
140 => float bpm; 1::second * 15 / bpm=> dur tick; fun float square(float f) { return (Math.floor(Math.sin(f))+0.5)*2.0; } fun float adsr(float t, float a, float d, float s, float r, float gt) { return Math.max(0.0, Math.min(1.0, t/Math.max(0.0001, a)) - Math.min((1.0 - gt) ,Math.max(0.0, t - a)*(1.0 - gt)/Math.max(0.0001, d)) - Math.max(0.0, t - s)*gt/Math.max(0.0001,r)); } class MyKick extends Chugen { 100 => float time; samp/second => float dt; fun float tick(float in) { Math.cos(315*time-10*Math.exp(-50*time)+0.3)*adsr(time,0,0.3,0,0,0) => float g; time+dt=>time; return g; } } class MySnare extends Chugen { 100 => float time; samp/second => float dt; fun float tick(float in) { square(3063.0*time*Math.sin(time*8000.0))*adsr(time,0.005,0.08,0,0,0) => float g; time+dt=>time; return g; } } class MyCloseHihat extends Chugen { 100 => float time; samp/second => float dt; fun float tick(float in) { square(3063.0*time*Math.sin(time*8000.0)) * adsr(time,0.005,0.08,0,0,0) => float g; time+dt=>time; return g; } } class MyOpenHihat extends Chugen { 100 => float time; samp/second => float dt; fun float tick(float in) { square(2763*time*Math.sin(time*8500)) * adsr(time,0.0,0.05,0.03,0.03,0.5) => float g; time+dt=>time; return g; } } fun void kick(){ MyKick o => Gain g => dac; 0.3 => g.gain; [0x0c05,0x0c05,0x0c05,0x0c0c]@=> int seq[]; 0 => int bar; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0=>o.time;} 1::tick => now; } bar++; } } fun void snare(){ MySnare o => Gain g => dac; 0.2 => g.gain; [0x9290,0x9290,0x4290,0x4292]@=> int seq[]; 0 => int bar; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => o.time;} 1::tick => now; } bar++; } } fun void closeHihat(){ MyCloseHihat o => Gain g => dac; 0.2 => g.gain; 0 => int bar; [0x5555,0x5555,0x5555,0x5155]@=> int seq[]; [0x3030,0x3030,0x3030,0x3030]@=> int vel[]; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => o.time;} if (vel[bar%4]>>i&1==1){0.3 => g.gain;}else{0.2 => g.gain;} 1::tick => now; } bar++; } } fun void openHihat(){ MyOpenHihat o => Gain g => dac; 0.2 => g.gain; 0 => int bar; [0x0000,0x0000,0x0000,0x0400]@=> int seq[]; [0x3030,0x3030,0x3030,0x3030]@=> int vel[]; while( true ){ for(int i; i < 16; i++) { if (seq[bar%4]>>i&1==1){0 => o.time;} if (vel[bar%4]>>i&1==1){0.3 => g.gain;}else{0.2 => g.gain;} 1::tick => now; } bar++; } } spork ~ kick(); spork ~ snare(); spork ~ closeHihat(); spork ~ openHihat(); 3::minute=> now;
ここで使っているシーケンスについての説明は
ここにあります。
メモ
俺的、kick音源、暫定一位は
— gaz (@gaziya5) 2019年5月25日
sin(pow(1.0-min(1.0,t*3.5),15.)*48.0+1.3)*exp(-t*3.0)
これ。+1.3は何だろうね。昔、FL1NEくんが熱くkickについて語っていたのが、わかるレベルには到達できたと思う。
--shader(GLSL)の小技 19-- 敷居の低いmusic shader
グラフィクを書いていると音楽をつけたくなります。だけど、shaderで音を出すのは敷居が高すぎる。
そこで、敷居だけは低いByteBeatを紹介します。敷居は低いけど奥は深すぎです。でも。お気楽に手を出すのはには、ちょうどいい。
説明は、難しいのと、忘れてる部分がいっぱいあるので、ひたすらリンクを張りまくります。
ポイントは
(t*9&t>>4|t*5&t>>7|t*3&t/1024)-1
ここを書き換えるだけ。
shaderはこちらです。#bytebeathttps://t.co/z8GizlEdQb
— gaz (@gaziya5) 2017年9月25日
byte beatの作り方を解説しています。ただ、逆ポーランド記法なので、よくわからないけど、理屈はわかります。 0-0-0-0.blog.so-net.ne.jp
このページはオンラインでbytebeatが出来ます。
最近、ShaderBoyがmusic shaderに対応したので、そちらのリンクも張っておきます。 ShaderBoyはオンラインGLSL(shadertoy対応)エディターです。
ShaderBoy has been updated.https://t.co/BE679kWPTN
— iY☁Yi (@iY0Yi) 2019年5月7日
ShaderBoy is a simple text editor that lets you write "Shadertoy" shaders with useful functions.
What's New:
*multipass shader
*Movie Export
*Timeline GUI
*Debug Knob GUI
*Shader List GUI
*And other GUI updates#Shadertoy pic.twitter.com/4YMFBQbkll
今回のshader (shadertoy rule)
vec2 mainSound( float time ) { int t = int(time * 8000.0); t = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; return vec2(float(t & 0xff - 128) / 128.); }
--shader(GLSL)の小技 18-- 距離関数専用エディターを移植
はてなブログでjavascriptが使えるようなので、以前、jsdo.itに書いた距離関数専用エディターを移植した。 たぶん雰囲気で使えると思います。
サンプルを幾つか用意しました。リストで距離関数を選択、エディターの中を適当に編集して、runボタンを押して遊んで見てください。
サンプルは、当時書いたままで、手直ししていないけど、距離関数の作り方の雰囲気はわかると思います。
リストの中に幾つかのサンプルがあります。
このエディターには、保存機能が付いてません。良い距離関数が出来たら、コピペで、どこかに移してください。
距離関数専用エディター (javascript)
--shader(GLSL)の小技 17-- twitterに書いた小技のリンク集
小出しだけど、twitterに小技を書いてきた。バラバラになっていたので、まとめてみた。
スムースabs()の作り方。floatをpとした時
— gaz (@gaziya5) May 8, 2019
abs(p)は
p-2.0*min(0.0,p)
と置き換えられる。なので#define smin(a,b,k) -log2(exp2(-k*a)+exp2(-k*b))/k
p-2.0*smin(0.0,p,32.0)
とかで、折り畳みの時の折り目を解消。
ちょっと閃いた。
— gaz (@gaziya5) May 8, 2019
step()をsmoothstep()変えたスムースabs()#define smoothabs(p,k) p-2.0*smoothstep(k,-k,p)*p
折り目が、くびれた感じになる。使えなくもない。 https://t.co/tgRb2f7vPf
twitterに載せれるスムースノイズ。vec2バージョン。
— gaz (@gaziya5) May 7, 2019
float n(vec2 p){
vec2 r=vec2(1,99),s=dot(floor(p),r)+vec2(0,r.y);
p=smoothstep(0.,1.,fract(p));
vec2 a=mix(fract(sin(s)*5555.),fract(sin(s+1.)*5555.),p.x);
return mix(a.x,a.y,p.y);
}
shaderは0~1の範囲のコントロールが基本。これさえ意識していけば、迷子にならずにすむ。この範囲同士の乗算はこの範囲だし、pow()を使ってもこの範囲ですむ。exp()も使いやすいし、出力の色もこの範囲。この範囲以外の数字も出てくるが、それはスケールと思えば問題ない。負数は-1倍と思えばいい。
— gaz (@gaziya5) May 3, 2019
スムースなノイズの考え方は、2つの座標を用意して、その座標をシーズにして乱数を取り、2つの座標の間の補間をsmoothstep()等で滑らかにする事。これをアルゴリズムにしたのが、パーリンノイズ。こらが解って関数を眺めれば、雰囲気がみえてくる。
— gaz (@gaziya5) May 3, 2019
nusanの法線算出
— gaz (@gaziya5) April 30, 2019
vec2 off=vec2(0.01,1);
vec3 n=normalize(map(p)-vec3(map(p-off.xyy),map(p-off.yxy),map(p-off.yyx)));
普段、片側だけ微分するのは嫌ってるけどlive codingは記述速さ優先だから、これは、使ってみようと思う。
hsvの関数の替わりにするならhttps://t.co/ze4bBnYuUY
— gaz (@gaziya5) April 27, 2019
これなら空で書ける。
shader showdownの決勝の一瞬にトンネルのskillがわかりました。@sp4ghet から情報をもらいテストしてみました。https://t.co/qzxLmQuPvH
— gaz (@gaziya5) April 23, 2019
vertex shaderで距離関数からmeshを作ってみた。
— gaz (@gaziya5) April 18, 2019
SurfaceNet test https://t.co/l9tg4Zzrh7 @CodePenさんから
mat3のx,y,z軸用の回転行列を、いちいち作るより、2Dの回転行列一個で処理した方が記述が短くて済みます。
— gaz (@gaziya5) April 12, 2019
mat2 rot=mat2(cos(a), sin(a), -sin(a), cos(a));
p.xy=rot*p.xy;
p.yz=rot*p.yz;
p.zx*=rot*p.zx;
0~1の範囲を-1~1に変えたいときは末尾に
— gaz (@gaziya5) April 12, 2019
*2.0-1.0
-1~1の範囲を0~1に変えたいときは末尾に
*0.5+0.5
2.0*fract(t)-1.0や0.5*sin(t)+0.5
よりfract(t)*2.0-1.0やsin(t)*0.5+0.5の方が慣れると楽です。
レイベクトルrd、レイの原点ro、座標をpとした時、rdとpの距離は
— gaz (@gaziya5) April 12, 2019
length(cross(rd,p-ro))
これで取得できる。これに制限を付けてraymarchigをして負荷軽減。
イメージで言えばバウンディング球みたいな奴。
三角波の応用で3Dビリーヤード状態にオブジェクトを動かすskill。あたかもcompute shaderでも使ってると思わせれる。
— gaz (@gaziya5) April 12, 2019
位置座標をp、移動ベクトルをv、時間をt、範囲をsとした場合。
vec3 p = (abs(fract(v*t)*2.0-1.0)*2.0-1.0)*s;
GPU負荷軽減skill。vertex shaderで複数のbillboardを作りその中でraymarchigして一つの画面に収めるskillがある。似たような事が一枚shaderでも出来る。
— gaz (@gaziya5) April 12, 2019
if(length(cross(rd,p-ro))<2.0){}
カッコのなかに一個のオブジェのmarchig処理を書いて、これを複数作りZソートする。https://t.co/l1bNTcrcyn
p=mod(p,6.0)-3.0;で折り畳んで現れる大量オブジェの間引きskill。各マス目のidをvec3 u=floor(p/6.0);で取得。このuをシーズにして乱数を取得。例えば0.5以上なら距離関数を1.0にして返せば間引き出来ます。ミニ化してるshaderだけど、雰囲気は解ると思います。https://t.co/ZQRaqTVDgm
— gaz (@gaziya5) April 11, 2019
遠くを黒っぽくするtrick。ガウシアンを使う。marching loopで手に入る距離をt、オブジェクトの色をcolとした場合。
— gaz (@gaziya5) April 11, 2019
col*=exp(-0.001*t*t);
この定数は絵を見ながら調整していく。fogというskillだと認識してたけど、もしかして空気遠近法と呼ばれてるかもしれない。使い方次第では、白くも出来る。
クォータニオンに対する反応が意外にあったので、類似で、
— gaz (@gaziya5) April 11, 2019
クォータニオンと同等の回転関数。
vec3 rotate(vec3 p,vec3 axis,float theta){
vec3 v=cross(axis,p),u=cross(v,axis);
return u*cos(theta)+v*sin(theta)+axis*dot(p,axis);
}
最近はクォータニオンを使っていないけど
— gaz (@gaziya5) April 11, 2019
vec4 quaternion(vec3 n, float a){
return vec4(n*sin(a/2.0),cos(a/2.0));
}
vec3 qtransform(vec4 q, vec3 p){
return p+2.0*cross(cross(p,https://t.co/J2rl9Xr9Oj)-q.w*p,https://t.co/J2rl9Xr9Oj);
}
これで出来る。引用元は忘れました。どこだっけ?
初めて知ったけど、palettesと言うトリックは、面白いね。HSVでの色付けに飽きてきたら、試す価値アリです。https://t.co/G4RFCOecmF
— gaz (@gaziya5) February 20, 2019
添削を受けました。https://t.co/rSgJ2kJfz0
— gaz (@gaziya5) 2019年2月18日
float g = (func(p.x+e)-func(p.x-e))/(2.0*e);
は、
float g = (func(p.x+e)-func(p.x-e))/(PI*e);
の方が良好のようです。
このロジックは試行錯誤で偶然見つけたものです。解る方がいましたら理屈を教えてください。さて用意したshaderどうしよう。 https://t.co/FvVaLSsS1A
smoothstepの小技。
— gaz (@gaziya5) February 15, 2019
1.0-smoothstep(a,b,x)
smoothstep(b,a,x)
は、同等。第1引数と第2引数の順番を入れ替えるだけ。
連続値をtimeで増やしていくより、FM音源のスキルを頂いて
— gaz (@gaziya5) February 9, 2019
sin(0.5*time+0.3*sin(0.2*time))
みたいにすると楽しい。フーリエ変換で絵を書いていくのも、こういうスキルの延長なんだよね。きっと。
shaderにストップモーションを入れていると、又、落ちたな。のヒヤヒヤ感が増強される。
— gaz (@gaziya5) February 9, 2019
time=min(8.0, mod(time,9.0));
こんな使い方。
魚眼レンズが素敵。
— gaz (@gaziya5) February 9, 2019
vec3 rd= normalize(vec3(uv,(1.0-dot(uv, uv)*0.5)*0.5));
レイマーチングは単にレイがオブジェクトに衝突するかの判定しかしない。難しいと感じるのは、レイの作り方の説明不足のように思う。shaderのスクリーンをuvに展開してnormalize(vec3(uv,-2)) これがレイだよって言えばいいと思う。後は、カメラの向きを変えたり、色を付けのスキルが、あるだけ。
— gaz (@gaziya5) February 5, 2019
mengerの距離関数の中に出てくる
— gaz (@gaziya5) January 23, 2019
if (p.x<p.y){ p.xy = p.yx;}
は、y=xの直線で折り畳んでいる意味だよね。なんとなくmengerのコーナーの細工を感じる。Shane氏は、これを
p.xy += step(p.x, p.y)*(p.yx - p.xy);
と書いている。shaderならではの書き方だ。
0.5773と丁寧に書いてあるから、深読みしちゃったけど、
— gaz (@gaziya5) January 12, 2019
vec3 calcNormal(vec3 p )
{
vec3 n=vec3(0);
for( int i=ZERO; i<4; i++ )
{
vec3 e=vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-0.5;
n+=e*map(p+e*0.001);
}
return normalize(n);
}
最後normalize()だし、これで問題ないでしょ。
法線算出の面白いコード発見。
— gaz (@gaziya5) January 12, 2019
vec3 calcNormal( in vec3 pos )
{
return normalize( cross(dFdx(pos),dFdy(pos)) );
}
でも、ちょっと粗いです。https://t.co/36doOhwq3U
たぶん、これが最古のtetrahedron normal。
— gaz (@gaziya5) January 11, 2019
四面体で法線算出。https://t.co/FxZ2UH9WhX
2013/02/25だって。知らなかった。
今のshadertoyには、物凄い数のshaderがアップされていて、掘り起こすの大変だぞ。
映像が暗い感じのshaderを一番最後にガンマ補正をかけて、明るくしてもらいたいと思う時がある。
— gaz (@gaziya5) January 8, 2019
col = pow(col, vec3(0.7));
とか。逆に暗くしたければ
col = pow(col, vec3(1.5));
とか。
textureをトンネル風にする小技。inversesqrt()を使います。shadertoyにコピペでどうぞ。
— gaz (@gaziya5) January 8, 2019
vec2 uv=(fragCoord*2.-iResolution.xy)/iResolution.y;
float d=inversesqrt(length(uv));
uv=vec2(d,atan(uv.y,uv.x)/1.57);
vec3 col=(2.-d)*textureLod(iChannel0,uv,0.).xyz;
fragColor=vec4(col,1.);
豆知識。
— gaz (@gaziya5) December 25, 2018
<canvas/ id="canvas" >と書いた後なら、javascriptでcanvas.widthとidが変数名として使えます。
同様に<script id="vs" type="x-shader/x-vertex">...</script>
の中の文字列は、この記述の後ならのjavascriptでvs.textで取り出せます。
shadertoyで録画したwebmをtwitterに載せれるmp4にするコマンド。
— gaz (@gaziya5) December 23, 2018
ffmpeg.exe -i capture.webm -vf fps=30 -pix_fmt yuv420p capture.mp4
豆知識。pow(x,0.5)とsqrt(x)は同等です。
— gaz (@gaziya5) December 22, 2018
豆知識。GLSLのsin()は引数をmod(i,6.283)とかしてません。sin()の引数にtimeとか入れて放置しておくと、おかしなことになってます。pow(-1.0,0.5)とか負数の小数乗は虚数になるようです。GPUによっては、それを無視せずに、それ以前の計算を放棄する奴もいるみたい。注意です。同様に負数のsqrt()もNG
— gaz (@gaziya5) December 22, 2018
ちょっと荒いけど、boxのエッジを取り出す方法。
— gaz (@gaziya5) December 17, 2018
col = vec3(0.8,0.3,0.1)*min(1.0,fwidth(col.x*5.0)*0.5);
こいつでやってます。(webGL2.0用)https://t.co/9MSlhlhdEd
久々に宣伝だ。昔作った、DEエディタ。簡易的に距離関数をつくれる奴。https://t.co/9wVzzbwC5l
— gaz (@gaziya5) December 11, 2018
俺が好んで使っている。2D形状を押出してエッジに丸みをつける距離関数。
— gaz (@gaziya5) December 11, 2018
float map(in vec3 p)
{
float de2D = length(p.xy)-1.0;
float depth = 0.5;
float round =0.2;
return length(max(vec2(de2D, abs(p.z)-depth),0.0))-round;
}
これがあると、2D距離関数が楽しくなるよ。
シーケンスは
— gaz (@gaziya5) December 5, 2018
float Q(float a,float b){
return step(iTime,a+b)*max(0.0,iTime-a);
}
こいつを使いました。world time と local time と言う考え方。
Q(5,10) は開始5秒から0から始まるlocal timeが始まり10秒経過したらlocal timeが0になる。我ながら良いアイデアです。
シーケンスでフラグ制御は
— gaz (@gaziya5) December 5, 2018
e=step(1.0,iTime)
+step(3.0,iTime)
+step(5.0,iTime)*2.0
-step(7.0,iTime);
これです。開始時は0、1秒後に1になり、3秒後に2になり、5秒後に4になり、7秒後に3になる。これで色々と時間管理が出来る。
--shader(GLSL)の小技 16-- 湾曲と補正
距離関数に入れる前の座標を湾曲させます。単純にすると歪がでるので、それを補正します。
ここにも載っていますが、違う方法でやってみます。
まず、プロットに使うような関数を用意します。
float func(float x) { float t = iTime*3.0; return 0.2*sin(x*3.0+t)+0.2*sin(x*2.0+t); }
これで空間を歪めます。
p.y -= func(p.x);
こうです。次に、この関数を微分します。微分は極小の勾配なので、
float e = 0.01; float g = (func(p.x+e)-func(p.x-e))/(2.0*e);
こうなると思っていたら、添削を受けました。
GLSL Sandbox をsandboxにupしたらforkされて
GLSL Sandbox こうなりました。
float g = (func(p.x+e)-func(p.x-e))/(PI*e);
この方が良さそうです。まあ、見た目の話ですので、どちらでも良いとは思いますが、お好みでどうぞ。
このg
を使って補正をかけます。
p.y *= cos(atan(g));
これについては、やはり適当にやって落ち着いた式です。数学的根拠はありません。良さげなので使ってます。 以前
Shader - Shadertoy BETA
ここで、ツッコまれました。
これで、空間の湾曲と補正が終わりです。あとは普通に距離関数を使うだけです。
この応用で
vec2 bend(vec2 p, float d) { p.y -= d*p.x*p.x; p.y *= cos(atan(2.0*d*p.x)); return p; }
こんなモノも作りました。ベジェ曲線はコストが高いので、2次関数で湾曲させます。ただし、両端は歪が取れないので誤魔化す様に使ってます。
また、2次元の湾曲もできます。
p.zy -= func(p.x); vec2 g = (func(p.x+e)-func(p.x-e))/(PI*e); p.zy *= cos(atan(g));
こんな感じで処理すれば、良いと思います。
このやり方でアーティファクトが出るので距離関数の出力に補正(*0.8)をかけてます。
return deSeg(p,2.0,0.2) *0.8;
アーティファクトの対処法として、1以下の数字を乗算する方法と、もう一つ
return min(0.4, deSeg(p,2.0,0.2));
なんてのもアリです。
今回のshader (shadertoy rule)
mat2 rotate(float a) { return mat2(cos(a),sin(a),-sin(a),cos(a)); } float func(float x) { float t = iTime*3.0; return 0.2*sin(x*3.0+t)+0.2*sin(x*2.0+t); } float deSeg(vec3 p, float h, float r) { p.x -= clamp( p.x, -h, h ); return length( p ) - r; } #define PI radians(360.0) float map(vec3 p) { float e = 0.01; p.y -= func(p.x); // 微分する。 float g = (func(p.x+e)-func(p.x-e))/(PI*e); // 補正 p.y *= cos(atan(g)); return deSeg(p,2.0,0.2) *0.8; } vec3 doColor(vec3 p) { return vec3(0.3,0.5,0.8); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv=(fragCoord*2.0-iResolution.xy)/iResolution.y; vec3 ro=vec3(0,3,5); ro.zx*=rotate(iTime*0.1); vec3 rd=normalize(vec3(uv,2)); vec3 w=normalize(-ro); vec3 u=normalize(cross(w,vec3(0,1,0))); rd=mat3(u,cross(u,w),w)*rd; float d,i,t=0.0; vec3 p=ro; vec3 col=vec3(0); for(i=1.0;i>0.0;i-=1./50.0) { t+=d=map(p); if(d<0.001) { col=doColor(p); col*=i*i; break; } p+=rd*d; } fragColor=vec4(col,1.0); }