LoginSignup
3
1

More than 3 years have passed since last update.

2次元ディスタンスフィールド線まとめ

Last updated at Posted at 2020-02-11

はじめに

  • 2次元ディスタンスフィールドまとめページが肥大化したので別ページとして切り出し詳細にまとめ直しました。
  • 直線・線分のディスタンスフィールドは特殊で円のディスタンスフィールドに比べると複雑になっています。
  • 最大の特徴としては、円や正多角形のディスタンスフィールドは原点が”点”でしたが、原点が"線"や”線分”になっています。
  • 一般的に使用する線とは2点間を結ぶ『線分』のことを指しますが、『線分』のディスタンスフィールドを理解するには、まずは、永久に続く線『直線』のディスタンスフィールドを理解する必要がある為、『線分』のディスタンスフィールドから解説します。

直線

ディスタンスフィールドに(永久に続く)直線はあるが、基本的に線分(2点で区切られた直線)はない。
ので、結論としては、直線をif文で条件分けして実現するのが一番てっとり早い。
他には、テッセレーションで直線を線分に分割したり、四角形の対向する2辺を極限まで小さくする方法があるが、if文で分岐するのが一番楽でしょう。

線分の仕方もいくつあるが、直線の描画方法にもいくつかの種類がある。

横軸直線

xline.png
https://goo.gl/6qC4pN

    l=abs(p.y);
    p.x=0.;
    p*=vec2(0.,1.);
    p*=mat2(0.,0.,0.,1.);

縦軸直線

yline.png
https://goo.gl/C4y5To

    l=abs(p.x);
    p.y=0.;
    p*=vec2(1.,0.);
    p*=mat2(1.,0.,0.,0.);

任意角度直線

linerotate.png
https://goo.gl/2vbVh3

    p=vec2(p.x*cos(t)+p.y*sin(t));

任意ベクトル方向直線

vectline.png
https://goo.gl/zvTdVP

precision mediump float;
uniform vec2  m;       // mouse
uniform float t;       // time
uniform vec2  r;       // resolution
uniform sampler2D smp; // prev scene

float cross(vec2 v1, vec2 v2) {return v1.x*v2.y - v1.y*v2.x;}

void main(void){
    vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
    float l;
    l=abs(cross(p,normalize(vec2(3.,4.))));
    gl_FragColor = vec4(vec3(l), 1.0);
}

片側直線

absline.png

任意ベクトル

片方しか要らない場合はabsを外す

    l=cross(p,normalize(vec2(3.,4.)));

dotにすると90度回転、このまま使う場合もある

    l=dot(p,normalize(vec2(3.,4.)));

実際に使うときはマイナスの時は NaN,Infinite or 1として使う

  • NaN
l=l<0.?sqrt(l):l;
  • 1
l=l<0.?1.:l;
  • 1(三項演算子ではなく、stepを使ったバージョン、どっちが早い?)
l=step(l,0.) + step(0.,l)*l;
  • Infinite
l=l<0.?1./0.:l;

    l=p.y;

    l=p.x;

任意方向

l=p.x*cos(t)+p.y*sin(t);

線分

ディスタンスフィールドは、極座標系なので、基本的に線分は存在しない。
ので、以下の3つの方法のどれかを選択します。
イラストレーターの線の先端に相当

実装が簡単なので、glslでは丸形先端が使われることが多いですが、線分と線分の接合部分がおかしくなりがちなので、止めた方がいいです。

  • 90度反転した直線のディスタンスフィールドでマスク(バット先端)
  • 細長いカプセルで、太さを最小としたもの(丸形先端)
  • 細長い長方形で、太さを最小としたもの(突出先端)

バット先端

lineseg.png

  • 90度回転させた直線のディタンスフィールドをもう1個使ってマスクする方法

90度回転させた直線のディタンスフィールドは外積の代わりに内積を使えばいい

precision mediump float;
uniform vec2  m;       // mouse
uniform float t;       // time
uniform vec2  r;       // resolution
uniform sampler2D smp; // prev scene

float cross(vec2 v1, vec2 v2) {return dot(v1,vec2(v2.y,-v2.x));}

float lLine(vec2 p,vec2 v1,vec2 v2){
  vec2 v = v2 - v1;
  vec2 d = normalize(v); //direction
  p-=v1;
  return 1.-(1.-abs(cross(p,d)))*step(0.,length(v)*.5-abs(dot(p-v*.5,d)));
}

void main(void){
    vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
    float l;
    l=lLine(p,vec2(-.8,-0.8),vec2(.8,.8));
    gl_FragColor = vec4(vec3(l), 1.0);
}
  • dotだけで作りif文で分岐するバージョン
precision mediump float;
uniform vec2  m;       // mouse
uniform float t;       // time
uniform vec2  r;       // resolution
uniform sampler2D smp; // prev scene

float cross(vec2 v1, vec2 v2) {return dot(v1,vec2(v2.y,-v2.x));}

float lLine( vec2 p,vec2 v1, vec2 v2){
    vec2 v     = v1 - v2;
    vec2 p2p   = p - v2;
    vec2 p1p   = p - v1;
    vec2 pd    = normalize(vec2(v.y, -v.x));
    float proj = dot(pd, -p1p);
    float pr1  = dot( v, p2p);
    float pr2  = dot(-v, p1p);
    return (pr1 > 0.0 && pr2 > 0.0) ? abs(proj): 0.;
}

void main(void){
    vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
    float l;
    l=lLine(p,vec2(-.8),vec2(.8));
    gl_FragColor = vec4(vec3(l), 1.0);
}

丸形先端

liner.png

precision mediump float;
uniform vec2  m;       // mouse
uniform float t;       // time
uniform vec2  r;       // resolution
uniform sampler2D smp; // prev scene

const float PI  = acos(-1.);
const float TAU = PI * 2.;

float cross(vec2 v1, vec2 v2) {return dot(v1,vec2(v2.y,-v2.x));}
float sumabs(vec2 p){return abs(p.x)+abs(p.y);}
float maxabs(vec2 p){return max(abs(p.x), abs(p.y));}
mat2 rot(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);}
float line(vec2 p, vec2 v1, vec2 v2,int cap) {
    vec2 p1 = p  - v1;
    vec2 v  = v2 - v1;
    float t = dot(p1, normalize(v));
    vec2 pt = (p-((t<0.)?v1:v2))*rot(atan(v.x,v.y));
    return (t<0.||t>length(v))
     ? ((cap==1)?length(pt):(cap==2)?maxabs(pt):(cap==3)?sumabs(pt):1e10)
     : abs(cross(p1, normalize(v)));
}
float line(vec2 p, vec2 v1, vec2 v2) {return line(p,v1,v2,0);}

void main(void){
    vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
    float l;
    l=line(p,vec2(-.1,-.4),vec2(.4),1);
    gl_FragColor = vec4(vec3(l), 1.0);
}

先端の形状

  • cap=0 バット先端
  • cap=1 丸型先端
  • cap=2 突出先端
  • cap=3 矢印

参考

3
1
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
3
1