符号なし距離関数で作る極座標変換

# 極座標変換

ぐぐっても小難しい説明しか出てこないので、Photoshopの極座標フィルターの動画を見てみてください。
https://helpx.adobe.com/jp/photoshop/how-to/use-polar-coordinates-filter.html

ようするに、画面の中央の点を中心にぐるっと360度回転させる変換のことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/441157f1-ae6e-e917-dfc9-7d91abe862c6.png)
この画像を極座標変換するとこうなります。

## ユークリッド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/230d5cdd-831d-9878-69ce-2543dcd1a1db.png)

```glsl
vec2 mPolar(vec2 p){
  float a = atan(p.y,p.x);
  float r = 0.;
  r = length(p);
  return vec2(a/PI, r);
}
```

通常、この変換にはlength(p)すなわちユークリッド距離が使われています。が距離？ってことは符号なし距離関数に置き換えられるということなので置き換えてみましょう。

サンプルのように、ディスタンスフィールドの周りに、fbmで作った炎をまとわせたり、こいつ、結構使えます！

fbmで作った炎をまとわせるために、レイマーチングの法線を求めるように、前後の差分で勾配求めて法線を出している人(激重)を結構みかけますが、符号なし距離関数自体が法線なので、極座標変換で直接使えば法線を計算する必要がないです。もちろん、符号なし距離関数1つで出来ない場合はそのアプローチしかありませんがw

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

## マンハッタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/63b2e0f0-8376-48de-2bf5-9e6b7a598eb3.png)

```glsl
  r = sumabs(p);
```

## チェビシェフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/171b85e9-52dc-5256-c603-7a156302cb9e.png)

```glsl
  r = maxabs(p);
```

## ミンコフスキー(p=3)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/87bc9685-2b2e-b7ed-01a9-f880851dbd88.png)

```glsl
  r = lpnorm(p,3.);
```

## ミンコフスキー(p=0.5) アステロイド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/01784b0a-4367-cbf9-f256-f78ec2f841e1.png)

```glsl
  r = lpnorm(p,.5);
```

## 六角形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/8a81d28b-2955-fc5f-bfe6-120a4cc63803.png)

```glsl
r = lPoly(p,6.);
```

## 星

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/32359a46-8901-c7e8-0cee-9b692531c133.png)

```glsl
r = star(p,6.);
```

# その他
## 極座標の開始点と終了点のつなぎめが気になる？
シームレスに出来ますよ。興味のある方は以下の方法を試してみてください。
サンプルプログラムが長くなるので処理してません。

* 三角波を使って左右対称にする
* がむさんの、MipMapレベルを変えたホワイトノイズを重ね合わせるだけで
タイリング可能なfBmテクスチャを作れるという検証
 * https://twitter.com/gam0022/status/1097123362164244480
 * https://www.shadertoy.com/view/3djGWd

## 応用

fbmの調整によって水をまとわせてもよいし、スパークをまとわせてもいい、疑似ハイライトもできる

### スパーク
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/5d97bb13-9ba8-59f4-b8ba-60d0ee3a7487.png)

### 水しぶき＋疑似ハイライト

![babble.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/e136be87-e253-0b5d-82b1-eb346706d75f.gif)

疑似ハイライトは水泡の形を歪ませても追従する

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

mat2 rot(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);}

float Hash( vec2 p, in float s ){
    return fract(sin(dot(vec3(p.xy,10.0 * abs(sin(s))),vec3(27.1,61.7, 12.4)))*273758.5453123);
}

float noise(vec2 p,float s){
  vec2 i = floor(p);
  vec2 f = fract(p);
  s = s * s * (3.0 - 2.0 * s);
  return mix(
    mix(Hash(i + vec2(0.,0.), s), Hash(i + vec2(1.,0.), s),f.x),
    mix(Hash(i + vec2(0.,1.), s), Hash(i + vec2(1.,1.), s),f.x),f.y) * s;
}

float fbm(vec2 p){
  float v = 0.0;
  v += noise(p    , .1);
  v += noise(p*40., .2);
  return v;
}

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 dot2(vec2 p){return dot(p,p);}
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);}
vec2 mPolar(vec2 p){
  float a = atan(p.y,p.x);
  float r = 0.;
  r = length(p);
  r = sumabs(p);
  r = maxabs(p);
  r = minabs(p);
  r = lpnorm(p,3.);
  r = lpnorm(p,.5);
  r = lpnorm(p,abs(sin(t*.5)*4.));
  r = lPoly(p,6.);
  r = lStar(p*rot(PI),5.);
  return vec2(a/PI, r);
}


void main( void ) {
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  p=mPolar(p);
  p.y-=.8;
  float l = (1./((p.y - fbm( p + vec2(sin(t*.2),t*0.1))) *  50.));
  gl_FragColor = vec4( l * vec3( 0.75, 0.5, .05 ), 1.0 );
}
```