符号なし距離関数で作る魚眼レンズ効果

# はじめに
符号なし距離関数シリーズ第3弾です。
今回は、座標pに(1+距離関数)を掛けることによって実現できる魚眼レンズ効果で、色々な距離関数に置き換えてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/322bbf4f-ae71-2fb0-5294-ddcb4047fdd2.png)

前回までの内容はこちら

* [符号なし距離関数で作る色々なボロノイ](https://qiita.com/7CIT/items/4126d23ffb1b28b80f27)
* [符号なし距離関数で作る極座標変換](https://qiita.com/7CIT/items/589cba0a739b9d8803cf)

[2次元ディスタンスフィールドの一覧はこちら](https://qiita.com/7CIT/items/fe33b9b341b9918b6c3d)

# 元の画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/23ba66d1-7dad-76da-f3af-60b18cdf90b3.png)

歪み具合が分かりやすいように、10x10のグリッドを用意し、それぞれのグリッドに左上から左下に掛けて番号を振り、円を描いた画像を用意しました。

# 魚眼レンズ(fish eye)効果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/9148b23a-5504-1ee0-458b-7f717699a1c9.png)

```glsl
p*=(1.+length(p))*.5;
```

座標pに(1+ユークリッド距離)を掛けると魚眼レンズ効果になります。
ちょっと、コイツを掛けるとカッコよくなるので、結構使われていますね。

# マンハッタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/507c2c20-265f-ac2f-5076-2bc45bd24a23.png)

# チェビシェフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/93aaa338-80dc-4e2e-13a3-0e10f369f1d9.png)

# ミンコフスキー　(p=-∞)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/5b95b79f-b5fb-6d80-5332-c57ab2310cc8.png)

# ミンコフスキー　(p=0.5)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/79d58e74-aa2a-8abb-082a-71647fd127af.png)

# ミンコフスキー　(p=8)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/17ccb425-8cfe-f680-8201-62e7189c131c.png)

# 六角形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/4ddab74b-93cf-7fde-15e1-174d0fa83569.png)

# 星
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/322bbf4f-ae71-2fb0-5294-ddcb4047fdd2.png)

## 逆魚眼

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/a2d6fdf5-48ea-9031-14c0-39d20bce31b9.png)

```glsl
p*=(1.-length(p)*.25)*1.5;
```


中央を窪ませたい場合はマイナスすればよいです。

## 加算バージョンの魚眼

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/835e9ee5-e9f0-9406-fb3f-fe9639c04707.png)

```glsl
  p+=sign(p)*length(p)*.05;
```

加算する場合はsignが必要です。lengthだとマイナスがなくなる為

## 減算バージョンの魚眼(逆魚眼)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/2d46aa02-0a8b-6d3f-cd01-78df49a5e8d4.png)

```glsl
  p-=sign(p)*length(p)*1.5;
```

他、バリエーションは色々つくれますが、正直微妙です。

```glsl
  p-=sign(vec2(p.x,-p.y))*length(p);
  p+=sign(vec2(p.x,-p.y))*length(p);
  p-=length(p);
  p+=length(p);
```

## その他

魚眼レンズ効果と全く同じ距離関数を使うと距離関数の距離1を基点に歪ませることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/41d3c7ca-c8c1-2fa5-edf5-9315eff50da6.png)

```glsl
  p*=(1.+lStar(p*rot(PI),5.))*.5;
.
.
.
  l+= strokeInner(sd(lStar(p*rot(PI),5.)),.01);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/9148b23a-5504-1ee0-458b-7f717699a1c9.png)

デフォルトの魚眼レンズ効果でも同じことが起こってますね。

```glsl
  p*=(1.+length(p))*.5;
.
.
.
  l+= strokeInner(sd(length(p)),.01);
```

 
# サンプルプログラム
```glsl
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.;

#define saturate(x) clamp(x,0.,1.)
#define _tail2x(p,n) (mod(p,2.)-1.)

vec2 tail2x(vec2 p,float n){p*=n;return _tail2x(p,n);}
vec2 tailX2x(vec2 p,float n){p*=n;return vec2(_tail2x(p.x,n),p.y);}
vec2 tailY2x(vec2 p,float n){p*=n;return vec2(p.x,_tail2x(p.y,n));}

float cross(vec2 v1, vec2 v2) {return dot(v1,vec2(v2.y,-v2.x));}
mat2 rot(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);}

// signed distance
float sd(float d,float r){return r-d;} 
float sd(float d){return 1.-d;} 

float fill_na(float d){return step(0.,d);}
float fill(float d){return smoothstep(0.,0.01,d);}
float stroke(float d,float w){return 1.-smoothstep(w,w+0.01,abs(d));}
float strokeInner(float d,float w){return stroke(d-w,w);}
float strokeOuter(float d,float w){return stroke(d+w,w);}

float sumabs(vec2 p){return abs(p.x)+abs(p.y);}
float maxabs(vec2 p){return max(abs(p.x), abs(p.y));}
float minabs(vec2 p){return min(abs(p.x), abs(p.y));}
float lpnorm(vec2 p, float n){vec2 t=pow(abs(p),vec2(n));return pow(t.x+t.y,1./n);}

float lPoly(vec2 p,float n){
  float a = atan(p.x,p.y)+PI;
  float r = TAU/n;
  return cos(floor(.5+a/r)*r-a)*length(p)/cos(r*.5);
}

float lStar(vec2 p,float n){
  return min(lPoly(p,n*.5),lPoly(mod(n,2.)!=0.?vec2(-p.x,p.y):p*rot(TAU/n),n*.5));
}

float lStar(vec2 p,float n,float o){
  return (lPoly(p,n)-lPoly(p*rot(PI/n),n)*o)/(1.-o);
}

float lHeart(vec2 p) {
  p.y += .034;
  p *= 1.1;
  return sqrt(dot(p, p) - abs(p.x)*p.y);
}
float dot2(vec2 p){return dot(p,p);}

float sLocater(vec2 p){
  float l=stroke(sd(maxabs(tailX2x(p,8.))),.016)*stroke(p.y,.03);
  l+=stroke(sd(maxabs(tailY2x(p,8.))),.016)*stroke(p.x,.03);
  l+=stroke(p.x,.001);
  l+=stroke(p.y,.001);
  return l;
}

float pCheckers(vec2 p,float n){vec2 q=p*n;return mod(floor(q.x)+floor(q.y),2.0);}

float seg(vec2 p,float s,float k){p=abs(p*k);return max(s+p.x,s*.5+p.y+p.x);}
float seg7(vec2 p,int n,float s,float k){
    float l=5.,w=s*.5;
    vec2 q=p.yx,h=vec2(s,0.),v=vec2(-w,w);
    l = (n!=1 && n!=4                ?min(seg(q-h,s,k),l):l);
    l = (n!=1 && n!=2 && n!=3 && n!=7?min(seg(p-v,s,k),l):l);
    l = (n!=5 && n!=6                ?min(seg(p-w,s,k),l):l);
    l = (n!=0 && n!=1 && n!=7        ?min(seg(q  ,s,k),l):l);
    l = (n==0 || n==2 || n==6 || n==8?min(seg(p+w,s,k),l):l);
    l = (n!=2                        ?min(seg(p+v,s,k),l):l);
    l = (n!=1 && n!=4 && n!=7        ?min(seg(q+h,s,k),l):l);
    return l;
}
void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  vec2 P =p;
  float l=0.;
  //p*=(1.+length(p))*.5;
  //p*=(1.+sumabs(p))*.5;
  //p*=(1.+maxabs(p))*.5;
  //p*=(1.+minabs(p));
  //p*=(1.+lpnorm(p,.5))*.5;
  //p*=(1.+lpnorm(p,8.))*.5;
  //p*=(1.+lPoly(p,6.))*.5;
  p*=(1.+lStar(p*rot(PI),5.))*.5;
  vec2 q=tail2x(p,10.);
  l+= pCheckers(p,5.)*.25+.25;;
  l+= stroke(seg7(q-vec2(.3,0.),int(floor((p.x+1.)*5.)),.4,1.3),.5);
  l+= stroke(seg7(q+vec2(.3,0.),int(floor((1.-p.y)*5.)),.4,1.3),.5);
  l+= strokeInner(sd(length(p)),.01);
  //l+= strokeInner(sd(lStar(p*rot(PI),5.)),.01);
  l*=step(-1.,p.x)*step(p.x,1.)*step(-1.,p.y)*step(p.y,1.);
  l+=sLocater(P);
  gl_FragColor = vec4(vec3(l), 1.0);
}
```
* [動作サンプルはこちら](http://jp.wgld.org/js4kintro/editor/#precision%20mediump%20float%3B%0Auniform%20vec2%20%20m%3B%20%20%20%20%20%20%20%2F%2F%20mouse%0Auniform%20float%20t%3B%20%20%20%20%20%20%20%2F%2F%20time%0Auniform%20vec2%20%20r%3B%20%20%20%20%20%20%20%2F%2F%20resolution%0Auniform%20sampler2D%20smp%3B%20%2F%2F%20prev%20scene%0A%0Aconst%20float%20PI%20%3D%20acos(-1.)%3B%0Aconst%20float%20TAU%20%3D%20PI%20*%202.%3B%0A%0A%23define%20saturate(x)%20clamp(x%2C0.%2C1.)%0A%23define%20_tail2x(p%2Cn)%20(mod(p%2C2.)-1.)%0A%0Avec2%20tail2x(vec2%20p%2Cfloat%20n)%7Bp*%3Dn%3Breturn%20_tail2x(p%2Cn)%3B%7D%0Avec2%20tailX2x(vec2%20p%2Cfloat%20n)%7Bp*%3Dn%3Breturn%20vec2(_tail2x(p.x%2Cn)%2Cp.y)%3B%7D%0Avec2%20tailY2x(vec2%20p%2Cfloat%20n)%7Bp*%3Dn%3Breturn%20vec2(p.x%2C_tail2x(p.y%2Cn))%3B%7D%0A%0Afloat%20cross(vec2%20v1%2C%20vec2%20v2)%20%7Breturn%20dot(v1%2Cvec2(v2.y%2C-v2.x))%3B%7D%0Amat2%20rot(float%20a)%7Bfloat%20c%3Dcos(a)%2Cs%3Dsin(a)%3Breturn%20mat2(c%2C-s%2Cs%2Cc)%3B%7D%0A%0A%2F%2F%20signed%20distance%0Afloat%20sd(float%20d%2Cfloat%20r)%7Breturn%20r-d%3B%7D%20%0Afloat%20sd(float%20d)%7Breturn%201.-d%3B%7D%20%0A%0Afloat%20fill_na(float%20d)%7Breturn%20step(0.%2Cd)%3B%7D%0Afloat%20fill(float%20d)%7Breturn%20smoothstep(0.%2C0.01%2Cd)%3B%7D%0Afloat%20stroke(float%20d%2Cfloat%20w)%7Breturn%201.-smoothstep(w%2Cw%2B0.01%2Cabs(d))%3B%7D%0Afloat%20strokeInner(float%20d%2Cfloat%20w)%7Breturn%20stroke(d-w%2Cw)%3B%7D%0Afloat%20strokeOuter(float%20d%2Cfloat%20w)%7Breturn%20stroke(d%2Bw%2Cw)%3B%7D%0A%0Afloat%20sumabs(vec2%20p)%7Breturn%20abs(p.x)%2Babs(p.y)%3B%7D%0Afloat%20maxabs(vec2%20p)%7Breturn%20max(abs(p.x)%2C%20abs(p.y))%3B%7D%0Afloat%20minabs(vec2%20p)%7Breturn%20min(abs(p.x)%2C%20abs(p.y))%3B%7D%0Afloat%20lpnorm(vec2%20p%2C%20float%20n)%7Bvec2%20t%3Dpow(abs(p)%2Cvec2(n))%3Breturn%20pow(t.x%2Bt.y%2C1.%2Fn)%3B%7D%0A%0Afloat%20lPoly(vec2%20p%2Cfloat%20n)%7B%0A%20%20float%20a%20%3D%20atan(p.x%2Cp.y)%2BPI%3B%0A%20%20float%20r%20%3D%20TAU%2Fn%3B%0A%20%20return%20cos(floor(.5%2Ba%2Fr)*r-a)*length(p)%2Fcos(r*.5)%3B%0A%7D%0A%0Afloat%20lStar(vec2%20p%2Cfloat%20n)%7B%0A%20%20return%20min(lPoly(p%2Cn*.5)%2ClPoly(mod(n%2C2.)!%3D0.%3Fvec2(-p.x%2Cp.y)%3Ap*rot(TAU%2Fn)%2Cn*.5))%3B%0A%7D%0A%0Afloat%20lStar(vec2%20p%2Cfloat%20n%2Cfloat%20o)%7B%0A%20%20return%20(lPoly(p%2Cn)-lPoly(p*rot(PI%2Fn)%2Cn)*o)%2F(1.-o)%3B%0A%7D%0A%0Afloat%20lHeart(vec2%20p)%20%7B%0A%20%20p.y%20%2B%3D%20.034%3B%0A%20%20p%20*%3D%201.1%3B%0A%20%20return%20sqrt(dot(p%2C%20p)%20-%20abs(p.x)*p.y)%3B%0A%7D%0Afloat%20dot2(vec2%20p)%7Breturn%20dot(p%2Cp)%3B%7D%0A%0Afloat%20sLocater(vec2%20p)%7B%0A%20%20float%20l%3Dstroke(sd(maxabs(tailX2x(p%2C8.)))%2C.016)*stroke(p.y%2C.03)%3B%0A%20%20l%2B%3Dstroke(sd(maxabs(tailY2x(p%2C8.)))%2C.016)*stroke(p.x%2C.03)%3B%0A%20%20l%2B%3Dstroke(p.x%2C.001)%3B%0A%20%20l%2B%3Dstroke(p.y%2C.001)%3B%0A%20%20return%20l%3B%0A%7D%0A%0Afloat%20pCheckers(vec2%20p%2Cfloat%20n)%7Bvec2%20q%3Dp*n%3Breturn%20mod(floor(q.x)%2Bfloor(q.y)%2C2.0)%3B%7D%0A%0Afloat%20seg(vec2%20p%2Cfloat%20s%2Cfloat%20k)%7Bp%3Dabs(p*k)%3Breturn%20max(s%2Bp.x%2Cs*.5%2Bp.y%2Bp.x)%3B%7D%0Afloat%20seg7(vec2%20p%2Cint%20n%2Cfloat%20s%2Cfloat%20k)%7B%0A%20%20%20%20float%20l%3D5.%2Cw%3Ds*.5%3B%0A%20%20%20%20vec2%20q%3Dp.yx%2Ch%3Dvec2(s%2C0.)%2Cv%3Dvec2(-w%2Cw)%3B%0A%20%20%20%20l%20%3D%20(n!%3D1%20%26%26%20n!%3D4%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Fmin(seg(q-h%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n!%3D1%20%26%26%20n!%3D2%20%26%26%20n!%3D3%20%26%26%20n!%3D7%3Fmin(seg(p-v%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n!%3D5%20%26%26%20n!%3D6%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Fmin(seg(p-w%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n!%3D0%20%26%26%20n!%3D1%20%26%26%20n!%3D7%20%20%20%20%20%20%20%20%3Fmin(seg(q%20%20%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n%3D%3D0%20%7C%7C%20n%3D%3D2%20%7C%7C%20n%3D%3D6%20%7C%7C%20n%3D%3D8%3Fmin(seg(p%2Bw%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n!%3D2%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Fmin(seg(p%2Bv%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20l%20%3D%20(n!%3D1%20%26%26%20n!%3D4%20%26%26%20n!%3D7%20%20%20%20%20%20%20%20%3Fmin(seg(q%2Bh%2Cs%2Ck)%2Cl)%3Al)%3B%0A%20%20%20%20return%20l%3B%0A%7D%0Avoid%20main(void)%7B%0A%20%20vec2%20p%20%3D%20(gl_FragCoord.xy%20*%202.0%20-%20r)%20%2F%20min(r.x%2C%20r.y)%3B%0A%20%20vec2%20P%20%3Dp%3B%0A%20%20float%20l%3D0.%3B%0A%20%20%2F%2Fp*%3D(1.%2Blength(p))*.5%3B%0A%20%20%2F%2Fp*%3D(1.%2Bsumabs(p))*.5%3B%0A%20%20%2F%2Fp*%3D(1.%2Bmaxabs(p))*.5%3B%0A%20%20%2F%2Fp*%3D(1.%2Bminabs(p))%3B%0A%20%20%2F%2Fp*%3D(1.%2Blpnorm(p%2C.5))*.5%3B%0A%20%20%2F%2Fp*%3D(1.%2Blpnorm(p%2C8.))*.5%3B%0A%20%20%2F%2Fp*%3D(1.%2BlPoly(p%2C6.))*.5%3B%0A%20%20p*%3D(1.%2BlStar(p*rot(PI)%2C5.))*.5%3B%0A%20%20vec2%20q%3Dtail2x(p%2C10.)%3B%0A%20%20l%2B%3D%20pCheckers(p%2C5.)*.25%2B.25%3B%3B%0A%20%20l%2B%3D%20stroke(seg7(q-vec2(.3%2C0.)%2Cint(floor((p.x%2B1.)*5.))%2C.4%2C1.3)%2C.5)%3B%0A%20%20l%2B%3D%20stroke(seg7(q%2Bvec2(.3%2C0.)%2Cint(floor((1.-p.y)*5.))%2C.4%2C1.3)%2C.5)%3B%0A%20%20l%2B%3D%20strokeInner(sd(length(p))%2C.01)%3B%0A%20%20%2F%2Fl%2B%3D%20strokeInner(sd(lStar(p*rot(PI)%2C5.))%2C.01)%3B%0A%20%20l*%3Dstep(-1.%2Cp.x)*step(p.x%2C1.)*step(-1.%2Cp.y)*step(p.y%2C1.)%3B%0A%20%20l%2B%3DsLocater(P)%3B%0A%20%20gl_FragColor%20%3D%20vec4(vec3(l)%2C%201.0)%3B%0A%7D)