はじめに
ディスタンスフィールド(ファンクション)で円を描くのはとても簡単です。
https://goo.gl/zhk7Me
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); // 正規化
float l = step(0.,.8-length(p));
gl_FragColor = vec4(vec3(l), 1.);
}
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); // 正規化
float l = length(p);
l = step(.75,l)*step(l,.8);
gl_FragColor = vec4(vec3(l), 1.);
}
じゃあ、線をドット(円)にしたいんだけど
これは、上の図形を描くに至るまでの物語である....。
極座標でのアプローチ
普通の破線は極座標を使えば簡単ですね。
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); // 正規化
float l = step(abs(.65-dot(p,p)),step(sin(atan(p.y,p.x)*60.),0.)*.05);
gl_FragColor = vec4(vec3(l), 1.);
}
この方法を使ってドットの破線を試みてみましょう。
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); // 正規化
float l;
l = step(abs(.7-dot(p,p)),sin(atan(p.y,p.x)*50.)*.05);
gl_FragColor = vec4(vec3(l), 1.);
}
なんとなく、ぽいのが出来ましたが、円が歪んでますね。
これはこれで味があっていいのですが、求めているのはこれではありません。
まぁ、極座標を使っているので、円の中心から離れるほどに横軸は広くなるし、縦軸は円形に歪んでいるので当然の結果と言えます。
ということは、縦軸と、横軸の歪みを補正してあげれば求める形に出来そうですが、処理的に無駄そうなので別のアプローチを考えましょう!
forを使ったアプローチ
precision mediump float;
uniform vec2 m; // mouse
uniform float t; // time
uniform vec2 r; // resolution
uniform sampler2D smp; // prev scene
#define PI 3.1415926
#define PI2 PI * 2.0
#define N 48
void main(void){
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); // 正規化
float l = 0.;
for(int i = 0; i < N; i++){
float o = float(i) / float(N);
float s = sin(o * PI2)*.8;
float c = cos(o * PI2)*.8;
l+= step(0.,.03-length(p+vec2(s,c)));
}
gl_FragColor = vec4(vec3(l), 1.);
}
for使えばできますよ。そりゃ簡単に
問題はglslのforはループ回数が固定なところ、例えば円の半径が変わるようなアニメーションをする場合に詰みます。
また、コードが長くなる。点の数が多くなると負荷が掛かるといった別の問題もあります。
- 参考 for文で円を描く https://qiita.com/doxas/items/00567758621bb506e584
新たなる力
そこで、新たなアプローチを考えます。
円のディスタンスフィールドの形状の上に円のディスタンスフィールドを乗せられないかと...。
そう、円のディスタンスフィールドの形状に円のディスタンスフィールド描けばいいじゃない!
しかし、ディスタンスフィールドは、別名距離関数と言われている通り、図形までの距離しか返してきません。つまりスカラーということで、ディスタンスフィールドを呼び出した時点で方向データ(ベクトル)が消失しています。ディスタンスフィールドから、さらに、その先でディスタンスフィールドを使うことは原理的に無理なのです。
だったら、ディスタンスフィールドを距離(スカラー)ではなくベクターで返したらいい。
第二話に続く...。