球状の座標をとることを考えてみる。
用途としては勾配法の初期値とかで三次元ベクトルのサンプリングをとる場合なんかに使う。
今回は3つの方法で見た目でわかりやすくしてみた。
正規化
まず球の半径が一定なことを利用して等間隔の3次元座標を正規化することを考える。
1 2 3 4 5 6 |
for(int i=-10;i<=10;i++)for(int j=-10;j<=10;j++)for(int k=-10;k<=10;k++){ GameObject s = GameObject.CreatePrimitive(PrimitiveType.Sphere); s.transform.parent = empty; s.transform.localScale*=0.02f; s.transform.position = new Vector3(i/10f,j/10f,k/10f).normalized; } |
これは明らかに粗密が分かれるのでサンプリングとしてよくない。
元が立方体の分布であることを考えると偏るのはしかたない。
正規化(改良)
そこで必要ない部分を削除してみる。
1 2 3 4 5 6 7 |
for(int i=-10;i<=10;i++)for(int j=-10;j<=10;j++)for(int k=-10;k<=10;k++) if(i==-10||i==10||j==-10||j==10||k==-10||k==10){ GameObject s = GameObject.CreatePrimitive(PrimitiveType.Sphere); s.transform.parent = empty; s.transform.localScale*=0.02f; s.transform.position = new Vector3(i/10f,j/10f,k/10f).normalized; } |
いい感じにサンプリングできている。
球座標
最後に正攻法として球座標を用いてサンプリングしてみる。
1 2 3 4 5 6 7 8 9 |
int n = 40; for(int i=0;i<=n;i++)for(int j=-n;j<=n;j++){ float theta=i*Mathf.PI/n, phi=j*Mathf.PI/n; GameObject s = GameObject.CreatePrimitive(PrimitiveType.Sphere); s.transform.localScale*=0.02f; float cos1 = Mathf.Cos(theta),cos2 = Mathf.Cos(phi); float sin1 = Mathf.Sin(theta),sin2 = Mathf.Sin(phi); s.transform.position = new Vector3(sin1*cos2,cos1,sin1*sin2); } |
正攻法ではあるけど極付近に偏りがあるのが気になる。