ツイッターで書いた以下の #つぶやきGLSL の作成ログ。
凄くゆるい花のマークを作りたいと思います。
#つぶやきGLSL #geeker
void main(void){
float N=2.*acos(-1.)/5.,j=N/4.+t,c,y=.4+sin(t)*.1;
vec2 p=(gl_FragCoord.xy*2.-r)/r,m=vec2(.0,.9);
for(int i=0;i<5;i++){
c+=step(length(p-vec2(cos(j),sin(j))*.5),y),j+=N;
}
gl_FragColor.rgb=length(p)<y+.1?m.yyx:m.yxx*c;
} pic.twitter.com/HsOUAFqRwV— Narumium (@Nr_Narumium) November 25, 2020
円状に円を描く
円状に円を5つ配置して中心を円で塗りつぶすイメージから始める。
for 文で角度を分割した逆数を表示してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
precision highp float; uniform vec2 resolution; uniform float time; void main(void){ float PI=acos(-1.),TAU=2.*PI,N=TAU/5.; vec2 r=resolution,p=(gl_FragCoord.xy*2.-r)/min(r.x,r.y); vec3 c; float j; for(int i=0;i<5;i++){ c+=.01/length(p-vec2(cos(j),sin(j))*.5); j+=N; } gl_FragColor=vec4(c,1); } |
for の中で右端から反時計回りに書いています。
for 文は順当に書くと
for(float i=0.;i<TAU;i+=N)
としたくなりますが、変数を使っているため
Loop index cannot be modified by non-constant expression
と怒られてしまいます(バージョンによっては大丈夫)。
while文も This type of loop is not allowed らしいのでこんな書き方に。
円の位置をずらして、大きさを調整します。
1 2 3 4 5 |
for(int i=0;i<5;i++){ float l=.01/length(p-vec2(cos(j+N/4.),sin(j+N/4.))*.5); c+=step(.03,l); j+=N; } |
寄り道
これは for 文を使わずに、ディスタンスフィールドだけで書くこともできます。
1 2 3 4 5 6 7 8 |
void main(void){ float PI=acos(-1.),TAU=2.*PI,N=TAU/5.; vec2 r=resolution,p=(gl_FragCoord.xy*2.-r)/min(r.x,r.y); float s=floor((atan(p.y,p.x))/N)*N+N/2.; vec2 q=vec2(cos(s),sin(s))*.5-p; float l=step(length(q),.3); gl_FragColor=vec4(vec3(l),1); } |
円を上に持ってくるには先と同様に N/2
の半分ずらせばいいのでこうする。
float s=floor((atan(p.y,p.x)+N/4.)/N)*N+N/4.;
for 文がない分スッキリして負荷も小さくなりそうですが、制限がありますし処理が複雑になると面倒なので今回は for 文のまま進めます。
色塗り
整理しつつ色を付けていきます。
1 2 3 4 5 6 7 8 9 10 |
void main(void){ float PI=acos(-1.),TAU=2.*PI,N=TAU/5.,j=N/4.,c; mat3 m=mat3(1.); vec2 r=resolution,p=(gl_FragCoord.xy*2.-r)/r; for(int i=0;i<5;i++){ c+=step(length(p-vec2(cos(j),sin(j))*.5),.3);j+=N; } gl_FragColor=vec4(m[0]*c,1); if(length(p)<.4)gl_FragColor=vec4((m[0]+m[1]),1); } |
懐かしさを覚えるシンプルな花ができた。
ごちゃごちゃと動きと色を追加してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
precision highp float; uniform vec2 resolution; uniform float time; #define C(c,p)c+=step(length(p-vec2(cos(j),sin(j))*.3),.25); void main(void){ float PI=acos(-1.),TAU=2.*PI,N=TAU/5.,j=N/4.+time,c,d,e; mat3 m=mat3(1.); vec2 r=resolution,p=(gl_FragCoord.xy*2.-r)/r,q,s; q=p+vec2(cos(time),sin(time*1.2))*.6; s=p+vec2(cos(time*1.2),sin(time))*.6; for(int i=0;i<5;i++){ C(c,p)C(d,q)C(e,s)j+=N; } if(c>0.)gl_FragColor=vec4(m[0]*c,1); if(d>0.)gl_FragColor=vec4(m[1]*d,1); if(e>0.)gl_FragColor=vec4(m[2]*e,1); if(length(p)<.3)gl_FragColor=vec4((m[0]+m[1]),1); if(length(q)<.3)gl_FragColor=vec4((m[1]+m[2]),1); if(length(s)<.3)gl_FragColor=vec4((m[0]+m[2]),1); } |
調性しつつgeekest(es300) にしてみてもこんな感じなのでこの方向性はしんどい。
1 2 3 4 5 6 7 8 9 10 |
#define C(c,p)c+=step(length(p-vec2(cos(j),sin(j))*.3),.25); #define D(c,a)if(c>0.)o=vec4(m[a]*c,1); #define E(p,a,b)if(length(p)<.3)o=vec4((m[a]+m[b]),1); float T=2.*acos(-1.),N=T/5.,j=N/4.+t,c,d,e; mat3 m=mat3(1.); vec2 p=(FC.xy*2.-r)/r,q,s; q=p+vec2(cos(t),sin(t*1.2))*.6; s=p+vec2(cos(t*1.2),sin(t))*.6; for(int i=0;i<5;i++){C(c,p)C(d,q)C(e,s)j+=N;} D(c,0)E(p,0,1)D(d,1)E(q,1,2)D(e,2)E(s,0,2) |
最初のイメージを活かして最小限動かすことにした。(geeker)
1 2 3 4 5 6 7 8 |
void main(void){ float N=2.*acos(-1.)/5.,j=N/4.+t,c,y=.4+sin(t)*.1; vec2 p=(gl_FragCoord.xy*2.-r)/r,m=vec2(.0,.9); for(int i=0;i<5;i++){ c+=step(length(p-vec2(cos(j),sin(j))*.5),y),j+=N; } gl_FragColor.rgb=length(p)<y+.1?m.yyx:m.yxx*c; } |