1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GPU で暖を取りたい人のための GLSLAdvent Calendar 2017

Day 2

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

Last updated at Posted at 2017-12-06

はじめに

ディスタンスフィールド(ファンクション)で円を描くのはとても簡単です。
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はループ回数が固定なところ、例えば円の半径が変わるようなアニメーションをする場合に詰みます。

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

新たなる力

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

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

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

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

第二話に続く...。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?