Imaginantia

思ったことを書きます

つぶやいたやつ 解説

文脈です。

www.pixiv.net

解説を書きます。基本的に試行錯誤9割で出来ています。


precision highp float;
uniform vec2 resolution;
uniform float time;
void main(){
    vec4 p=gl_FragCoord/resolution.y-1.2;
    float y=pow(p.y,2.);
    p/=y;
    for(int i=0;i<3;i++)p.xy*=mat2(5,1,3,5)*.3,p=abs(fract(p+time)-.5);
    gl_FragColor=vec4(cos(vec3(4,3,5)+y+pow(p.y*2.3,5.))+1.,1);
}
  • p が座標、y は奥行きで、圧縮すれば透視変換っぽいのではという適当な発想により pow(p.y,2.) を使っています。py で割っていい感じの座標系が手に入ります。
  • あとはIFSっぽい感じで。mat2(5,1,3,5)*.3 は最初は mat2(3,2,-2,3)/sqrt(13.) (純粋な回転) だったんですがいい感じのパラメータを探していったらこうなりました。乱数調整。
  • 最後に色をつけます。このiqさんのpaletteの作り方を参考に、適当に cos に突っ込みました。
    • cos は 2π≒6 の周期を持つ関数なので適当に整数を突っ込むだけでもいろんな色味を探せます。vec3(4,3,5) は RGB の差を表していて、後は y で奥行き分の色味、pow(p.y,2.3,5.) でさっき計算した模様をいい感じにパラメータに与えています。
    • あと cos の結果は -1 から 1 なので、1. のオフセットを載せてあげると 0 から 2 になっていい感じに光ります。行儀は悪いけどそんなこと言ってられないということで。

終わり。そういえば gl_FragCoord.xyxy がもったいないので全部 vec4 でやるということをやっていたけど効果あったのかはわかりません。


次です。

#define s e.xy*=mat2(9,-3,7,4)*.1,e=sin(e.xzy*2.)+.5
precision lowp float;
uniform vec2 resolution;
uniform float time;
void main(){
    vec3 p,e,d=p=vec3(gl_FragCoord.xy/resolution,1);
    p.z=time;
    for(int i=0;i<40;i++)e=p,s,s,s,p+=(e.z-.5)*d;
    gl_FragColor=vec4(cos(e.xyx-d),1);
}

IFSっぽいノリで距離関数を書いたが結局見た目はそうはならなかった例。私がもともとそういう絵を作ったことがないので調整力が無い問題。

弄る前はこんな感じだったと思います。

f:id:phi16_ind:20200416154858p:plain

precision highp float;
uniform vec2 resolution;
uniform float time;
float f(vec3 p) {
    for(int i=0;i<3;i++) p.xy*=mat2(1,1,-1,1)/sqrt(2.),p=abs(fract(p.xzy)-.5)-.5;
    return length(p)-.3;
}
void main(){
    vec3 p,d=p=vec3(gl_FragCoord.xy/resolution-.5,1);
    p.z=time;
    for(int i=0;i<40;i++)p+=f(p)*d;
    gl_FragColor=vec4(cos(p),1);
}

この時点で p を微妙にズレた場所に置いてしまうことで文字数を省略したり dnormalize をしないなどである程度稼ごうとしている感じですね。これはほぼ普通のraymarchのコード。

まず f を潰します。abs(fract(p.xzy)-.5)-.5 はいい感じに三角波を生成していますが、どうせ周期関数なので sin(p.xzy) にしました。当然シーンが崩壊するので周りの諸々をいじりながら調節するとこうなりました。

float f(vec3 p) {
    for(int i=0;i<3;i++)p.xy*=mat2(9,-3,7,4)*.1,p=sin(p.xzy*2.)+.5
    return p.z-.5;
}
  • 回転行列をちょっと変えても線形変換は線形変換なのでなんとかなるイメージ
    • 行列式が 0.57 で後に *2. して sin(x)≒xするので空間の大きさとかをいい感じに変えない状況になってるかも(雑感)
  • あと length(p) の代わりに p.z にしていますがこれも試行錯誤 (というより length が長いので短く済むようにシーンを弄っていった)
  • (実際にはこの調節はコードを書きつつ絶えずやっている感じなのであまりこの工程順に意味はありません)

f:id:phi16_ind:20200416160246p:plain

さて、関数とfor文がもったいないので潰します。

#define s e.xy*=mat2(9,-3,7,4)*.1,e=sin(e.xzy*2.)+.5
precision highp float;
uniform vec2 resolution;
uniform float time;
void main(){
    vec3 p,e,d=p=vec3(gl_FragCoord.xy/resolution-.5,1);
    p.z=time;
    for(int i=0;i<40;i++)e=p,s,s,s,p+=(e.z-.5)*d;
    gl_FragColor=vec4(cos(p),1);
}

関数内で p を弄っちゃってるので一度コピーしてあげる必要があります。ループはマクロでどうにかする。これで265chars。

あとは d の計算で -.5 してるの消しても別によくない?と思って消して、5文字追加して色味を整えて (いろいろやって cos(e.xyx-d) にした)、1文字足りなかったので lowp にして (敗北) 終わり。

f:id:phi16_ind:20200416164742p:plain

球がなんだか金属質な見た目になってかわいい。


あと最後。

#define m for(int i=0;i<15;i++)c=cos(e=p).z,e.xy*=mat2(c,c-1.,1.-c,c),e=sin(e*8.)*.2+.2,p+=(dot(e,e)-.1)*d;
precision highp float;
uniform float time;
void main(){
    float c;
    vec3 d,e,u,p=d=gl_FragCoord.xyw/2e2-.7;
    p.z+=time;
    m
    u=p=e;
    m
    gl_FragColor=vec4(cos(e+u.z*3.),1);
}

gyaboさんの作品とかFMS_Catさんの作品がレイを2回飛ばしていて羨ましかったので2回飛ばしたかったというモチベーションです。

precision highp float;
uniform float time;
float f(vec3 p){
  p.xy *= mat2(cos(p.z),sin(p.z),-sin(p.z),cos(p.z));
  for(int j=0;j<2;j++) p = fract(p.yzx)-.5, p.z-=0.1;
  return length(pow(abs(p),vec3(2.)))-.2;
}
void main(){
  vec3 d,p=d=gl_FragCoord.xyw/4e2-.7;
  p.z+=time;
  for(int i=0;i<19;i++)p+=f(p)*d;
  gl_FragColor=vec4(cos(p)*.5+.5,1);
}

ついに resolution を使うのをやめたみたいです。

f を潰していきます。

  • 回転行列書くのが長いのでとりあえず float c=cos(p.z), s=sin(p.z); p.xy *= mat2(c,s,-s,c); にしました。
  • ここで s=sqrt(1.-c*c) を考えましたが、なんとなく s=1.-c にしてみたところ大して問題がないことがわかります。
  • float c=cos(p.z);p.xy*=mat2(c,1.-c,c-1.,c); になりました。8文字減りました。

後は適当に書いた部分でどうでもよさそうなところを消しました。

float f(vec3 p){
  float c=cos(p.z);
  p.xy*=mat2(c,c-1.,1.-c,c);
  p = fract(p.yzx-.5)-.5;
  return dot(p,p)-.1;
}

f:id:phi16_ind:20200416162838p:plain

fract(p.yzx-.5)-.5 をなんとなく sin(p*8.)*.2+.2 にしました。3文字減ります。pow(abs(p),vec3(2.)) で角ばっている感じを出していたのが戻ってきてうれしい気持ち。

f:id:phi16_ind:20200416163033p:plain

あとはぐっとやります (242chars)。

precision highp float;
uniform float time;
void main(){
  float c;
  vec3 d,e,p=d=gl_FragCoord.xyw/4e2-.7;
  p.z+=time;
  for(int i=0;i<19;i++)e=p,c=cos(e.z),e.xy*=mat2(c,c-1.,1.-c,c),e=sin(e*8.)*.2+.2,p+=(dot(e,e)-.1)*d;
  gl_FragColor=vec4(cos(p)*.5+.5,1);
}

レイを飛ばす部分をマクロにして2度呼びます。どこから飛ばすかは何も考えてませんでしたが適当に弄っていたら e がそれっぽく使えたので使います。偶然マーブル感ある模様になっていい感じ。

#define m for(int i=0;i<19;i++)e=p,c=cos(e.z),e.xy*=mat2(c,c-1.,1.-c,c),e=sin(e*8.)*.2+.2,p+=(dot(e,e)-.1)*d;
precision highp float;
uniform float time;
void main(){
  float c;
  vec3 d,e,u,p=d=gl_FragCoord.xyw/4e2-.7;
  p.z+=time;
  m
  u=p=e;
  m
  gl_FragColor=vec4(cos(e+u.z*3.),1);
}

これで266chars。当初はこの時点だと足りなくて e=p,c=cos(e.z)c=cos(e=p).z にして2文字稼いでますね。しんどいね。

f:id:phi16_ind:20200416164828p:plain

お疲れさまでした。この記事の役に立たなさがすごい。ガチャ引きゲームに疲れたのでちゃんとvisual作りたいですね。