前回は線分を書けるようになったので、それを使って雷を書いていきます。
基本部分の線分を書くところは以下を見てください。
雷の形にする
従来の直線をN分割してそれぞれを違う方向に向けます。
分割した単位に使うベクトルを使って本来の終点位置を計算することで、大幅にずれることを防止しています。
ランダム関数はいつもの。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
float rand(vec2 co){ return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453); } #define NoC 5 void thunder(vec2 s,vec2 e){ vec2 _s=s;//区間始点 vec2 d=(e-s)/float(NoC);//単位にするベクトル for(int i=1;i<NoC;i++){ vec2 _e=s+d*float(i);//本来の直線での終点 _e+=vec2((rand(_e)-.5)*d.y,0);//ランダムにxをずらす line(_s,_e);//線を引く _s=_e;//区間始点更新 } line(_s,e);//終点への線分 } |
それっぽくなりましたが、よーく見ると曲がる部分に違和感がある。
接続点を自然にする
線分をつなげるときに隙間が出来てしまいます。
対処方法は色々あると思いますが円を使ってみます。
接続点は for
文の中で出てるので、適当なサイズの円を書きます。
線の大きさより若干大きめだとエッジが効いて見える。
1 2 3 4 5 6 7 8 9 10 11 |
void circle(vec2 pos,float size){ vec2 p=gl_FragCoord.xy/resolution; cs=max(step(length(pos-p),size),cs); } void thunder(vec2 s,vec2 e){ for(int i=1;i<NoC;i++){ ... circle(_e,4e-3); } } |
この書き方だと輪郭がくっきりするので、線の方に合わせてぼやけさす場合はこうします。こっちだと太さは線と同じにする。
1 2 3 4 |
void circle(vec2 pos,float size){ vec2 p=gl_FragCoord.xy/resolution; cs=max(pow(size/length(pos-p),3.),cs); } |
複数の雷を降らす
雷の線が1本なのが物足りない。
線の太さを変更できるようにして、複数本の雷を描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#define NoC 5 void thunder(vec2 s,vec2 e,float size,float nth){ vec2 _s=s; vec2 d=(e-s)/float(NoC); for(int i=1;i<NoC;i++){ vec2 _e=s+d*float(i); _e+=vec2((rand(_e*nth)-.5)*d.y,0);//線ごとのランダム値を変更 line(_s,_e,size); _s=_e; circle(_e,3e-3*size); } line(_s,e,size); } #define NoL 5 void thunders(vec2 s,vec2 e){ for(int i=1;i<NoL;i++){ thunder(s,e,4./float(i),float(i)); } } |
大分いい感じになってきたけどなんか当初考えたのと違うような。
途中で分岐させる
考えてみれば雷って途中で分岐してる気がする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#define NoC 6 void thunder(vec2 s,vec2 e,float size,int nth){ vec2 _s=s; vec2 d=(e-s)/float(NoC); for(int i=1;i<NoC;i++){ vec2 _e=s+d*float(i); float rnd=i>=nth?float(nth):1.; _e+=vec2((rand(_e*rnd)-.5)*d.y*rnd,0);//線ごとのランダム値を変更 line(_s,_e,size); _s=_e; circle(_e,3e-3*size); } line(_s,e,size); } #define NoL 5 void thunders(vec2 s,vec2 e){ for(int i=1;i<=NoL;i++){ thunder(s,e,4./float(i),i); } } |
大分思い通りになってきた。まだ終点が同じなのが気になる。
別れた枝は途中で消えてるイメージがある。
というわけで色々含めて微調整したコードがこれです。
最後なので全部載せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
precision highp float; uniform vec2 resolution; vec3 co=vec3(.7,.8,1.); float cs=0.; float rand(vec2 co){ return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453); } void circle(vec2 pos,float size){ vec2 p=gl_FragCoord.xy/resolution; cs=max(pow(size/length(pos-p),3.),cs); } void line(vec2 s,vec2 e,float size){ vec2 p=gl_FragCoord.xy/resolution; vec2 se=e-s,ps=s-p,pe=e-p; if(dot(ps,se)*dot(pe,se)>0.)return; if(s.x!=e.x){ float a=(e-s).y/(e-s).x; float d=abs(a*p.x-p.y-a*s.x+s.y)/sqrt(a*a+1.); cs=max(pow(size*3e-3/(d+1e-6),3.),cs); }else{ float d=abs(p.x-s.x); cs=max(pow(size*3e-3/(d+1e-6),3.),cs); } } #define NoC 10 #define NoL 8 void thunder(vec2 s,vec2 e,float size,int nth){ vec2 _s=s; vec2 d=(e-s)/float(NoC); for(int i=1;i<NoC;i++){ vec2 _e=s+d*float(i); float rnd1=i>=nth?float(nth):1.; float rnd2=i>=nth&&nth!=1?2.:1.; _e+=vec2((rand(_e*rnd1)-.5)*d.y*rnd2,0); line(_s,_e,size); if(nth!=1&&i-nth>0)return;//途中終了 _s=_e; circle(_e,3e-3*size); } line(_s,e,size); } void thunders(vec2 s,vec2 e){ for(int i=1;i<=NoL;i++){ thunder(s,e,4./float(i),i); } } void main(void){ vec2 p=gl_FragCoord.xy/resolution; thunders(vec2(.5,1.),vec2(.5,0.)); gl_FragColor=vec4(co*min(cs,1.),1.); } |
大体こんな感じ。
完全にランダムだとうまく1枚絵にはならないですね。
時間変数なんか使って動かすとこんな感じです。
後は枝分かれした部分の乱数を偏らせたり(左右に分かれたものをそのままの方向に)、先細りさせたりするともっと良い感じになると思います。
とりあえずは満足できたので雷はここまで。