glsl atan 仕様まとめ

# glslのatanの仕様は
 https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/atan.xhtml

重要な項目は***xが0の時返値はundefinedになる。***

数学的にy/x x/0はゼロ除算になるので当然と言えば当然なのですが、それだと色々困るので、端末の実装によっては勝手に0とかにしてくれているものが多い。でも、すべての端末でそういった処理をしてくれるわけではないので、トラブル回避の為x=0の場合の処理を入れておかないと事故る。他のプログラミング系では自動で0とかにしてくれる仕様。

* [参考](https://stackoverflow.com/questions/26070410/robust-atany-x-on-glsl-for-converting-xy-coordinate-to-angle)

以下のようなラッパー関数を作って0除算を回避する

glslのatanは、他プログラミング言語実装のatan2の180度以上の値についての処理はされているが、0除算だけ対応してない。

```glsl
float atan2(in float y, in float x){
    return x == 0.0 ? sign(y)*PI/2 : atan(y, x);
}
```

# 角度を-π～πの間で出力したい
```glsl
atan(p.x,p.y)
```

-π～πが返って来る。sinとかcosとか三角関数形の関数に使う場合はこのままの方が便利だが、画素の輝度に使いたい場合には都合が悪い

# 角度を-1～1の間で出力したい

単純にPIで割ればいい

```glsl
atan(p.x,p.y)/PI
```

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  float l =0.;
  l = atan(p.y,p.x)/PI;
  gl_FragColor = vec4(vec3(-l,max(l-1.,0.),l*step(l,1.)), 1.0);
}
```
* https://goo.gl/uBYq3a

![atan22.png](https://qiita-image-store.s3.amazonaws.com/0/135381/ea5a3a45-c3c7-d7e5-9a13-b4484b16f70a.png)

画素を表示するときには通常マイナスは使わないのであまり使われない

# 角度を0～1の間で出力したい

一番目にするパターンがこれ
atan(p.y,p.x)+PIを足して、マイナスを打消し、全体をPI2で割る

これ、当たり前だが、πを足しているので、開始位置が180度ずれる。
大抵の場合、回転しても問題ないものならこれで大丈夫なのですが、たまに回転するとまずいものがある。

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  float l =0.;
  l = (atan(p.y,p.x)+PI)/PI2;
  gl_FragColor = vec4(vec3(-l,max(l-1.,0.),l*step(l,1.)), 1.0);
}
```
![normal.png](https://qiita-image-store.s3.amazonaws.com/0/135381/7a18aa82-5b4e-ced4-968b-79ce5d2de51a.png)

# 角度を0～1の間で出力したい

おすすめはこちら、端数が出そうなPIの計算を極力抑え、開始位置も元に戻す

```glsl
fract(atan(p.y,p.x)/PI2+1.)
```

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  float l =0.;
  l = fract(atan(p.y,p.x)/PI2+1.);
  gl_FragColor = vec4(vec3(-l,max(l-1.,0.),l*step(l,1.)), 1.0);
}
```

![atansin.png](https://qiita-image-store.s3.amazonaws.com/0/135381/dab78297-2842-8644-9d5a-68ac63b48d7e.png)
* https://goo.gl/3Rnwqa

# 角度を0～1の間で出力したい(失敗 番外編)

```glsl
atan(-p.y,-p.x)/PI2+.5
```

こちらの方がシンプルに書け一見正常に動作しそうであるが....

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  float l =0.;
  l = atan(-p.y,-p.x)/PI2+.5;
  gl_FragColor = vec4(vec3(-l,max(l-1.,0.),l*step(l,1.)), 1.0);
}
```
* https://goo.gl/P6dmNL

![atan.png](https://qiita-image-store.s3.amazonaws.com/0/135381/06dc5e68-debb-93fb-8a3a-879adc281011.png)


関数にすると正常に動作しない
座標(1.0)を引数に渡した時に意図する答えは0だが1が返って来る

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

float angle(vec2 p){return atan(-p.y,-p.x)/PI2+.5;}

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

見た目は変わっていなさそうだが0と2π境界角度0のときの挙動が異なる

* https://goo.gl/iDsvme

今度は、関数化せず、直接値を入力してみる

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

const float PI  = 3.141592653589793;
const float PI2 = PI * 2.;

void main(void){
  vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
  float l =0.;
  l = atan(-0.,-1.)/PI2+.5;
  gl_FragColor = vec4(vec3(-l,max(l-1.,0.),l*step(l,1.)), 1.0);
}
```

正常に0が帰って来る

```glsl
  l = atan(-0.,-1.)/PI2+.5;
```

↓ -0を0に変えてみる

```glsl
  l = atan(0.,-1.)/PI2+.5;
```

試しに、-0を0にすると答えが1になる。
つまり、atanは0と-0で返り値が異なるが、変数を介すると-0は0に変換されてバグる。。。

0=２π で同じものであるが、それぞれ0と-0に変換されているので、p.xやp.yにマイナスを付けて反転させるとバグる