GLSL
で雷を書いてみようと思ったので1から書いていく。
とりあえず直線をと思って適当に書くと微妙に見た目が悪かったりするのが気になったので、まずはきれいに直線を書くことを考えます。
環境によっては draw()
とかで済ませられそうなものですが、GLSL
で書こうとするとちゃんと考える必要がありました。
線を引く(失敗
位置 s
から e
への線を引くことを考える。
点 p
が ベクトル es
上にあると以下の式が成り立ちます。
length(e-p)+length(p-s)=length(e-s)
これを使って線を引きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
precision highp float; uniform vec2 resolution; vec3 co=vec3(1,1,1); float cs=0.; void line(vec2 s,vec2 e){ vec2 p=gl_FragCoord.xy/resolution; float d=length(p-s)+length(e-p)-length(e-s)+1e-5; cs=1e-5/d; } void thunder(vec2 s,vec2 e){ line(s,e); } void main(void){ vec2 p=gl_FragCoord.xy/resolution; thunder(vec2(.55,1.),vec2(.45,0.)); gl_FragColor=vec4(co*cs,1.); } |
中央の方が太くなるのは判定が甘くなるからです。
線から等距離でも length(e-p)+length(p-s)
は中心で最も短くなって、端に行くほど長くなります。
d*=min(length(p-s),length(e-p));
などで調整すると線中心ほど大きく(逆数は小さく)なるので多少マシになります。
ちゃんと線を引く
きちんと描画したければきちんと式を立てる必要があります。
点 p
と直線の距離は次の式で表せる。
直線の式は次のように求める。
数式を解いてプログラムにするとこうなる。
1 2 3 4 5 6 7 8 |
void line(vec2 s,vec2 e){ vec2 p=gl_FragCoord.xy/resolution; 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=3e-3/(d+1e-6); } |
このままだと線から離れても少し明るいので累乗をかましたりする。
cs=pow(1e-3/(d+1e-6),3.);
綺麗な直線を引くことが出来ました。
複数の線を引く場合はこうします。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//line() cs=pow(1e-3/(d+1e-6),3.); ↓ cs=max(pow(3e-3/(d+1e-6),3.),cs); //thunder() void thunder(vec2 s,vec2 e){ line(s,e); line(vec2(.55,1.),vec2(.45,0.)); line(vec2(.6,1.),vec2(.5,0.)); line(vec2(.65,1.),vec2(.4,0.)); line(vec2(.7,1.),vec2(.45,0.)); } |
このコードだと縦の線(傾き∞)は書けないので注意。
外積をうまく使うともう少し簡単に書ける気がするけどどうだろう。
とりあえず今日はここまで。