Help us understand the problem. What is going on with this article?

[連載 GLSL 物語] チカラが欲しいか……(第一話)円を描く、但し、丸いドット点線で……

More than 1 year has passed since last update.

はじめに

ディスタンスフィールド(ファンクション)で円を描くのはとても簡単です。
circle.png
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.);
 }

circle2.png
https://goo.gl/hdjgbw

 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.);
 }

じゃあ、線をドット(円)にしたいんだけど

dot circle.png
https://goo.gl/ra2QGP

これは、上の図形を描くに至るまでの物語である....。

極座標でのアプローチ

普通の破線は極座標を使えば簡単ですね。

squarecircle.png
https://goo.gl/4D4x7g

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.);
}

この方法を使ってドットの破線を試みてみましょう。

sincircle.png
https://goo.gl/1eZYcy

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を使ったアプローチ

forcircle.png
https://goo.gl/tuoUG8

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はループ回数が固定なところ、例えば円の半径が変わるようなアニメーションをする場合に詰みます。

また、コードが長くなる。点の数が多くなると負荷が掛かるといった別の問題もあります。

新たなる力

そこで、新たなアプローチを考えます。
円のディスタンスフィールドの形状の上に円のディスタンスフィールドを乗せられないかと...。

そう、円のディスタンスフィールドの形状に円のディスタンスフィールド描けばいいじゃない!

しかし、ディスタンスフィールドは、別名距離関数と言われている通り、図形までの距離しか返してきません。つまりスカラーということで、ディスタンスフィールドを呼び出した時点で方向データ(ベクトル)が消失しています。ディスタンスフィールドから、さらに、その先でディスタンスフィールドを使うことは原理的に無理なのです。

だったら、ディスタンスフィールドを距離(スカラー)ではなくベクターで返したらいい。

第二話に続く...。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away