ゲーム数学の極座標系

この記事は、[数学ゲーム Advent Calendar 2018](https://adventar.org/calendars/3207) の6日目の記事です。


今年のCEDECの[こちらの公演](https://2018.cedec.cesa.or.jp/session/detail/s5ab884cadf368)がきっかけで、最近、DirectXを勉強し直している、ゲーム会社に勤めるエンジニアです。
DirectX 12を粛々と進めていたところ、ゲーム数学 Advent Calendarが空いているとの事を知り、やっていた事の振り返りも兼ねて、記事を投稿させていただきます。

### この記事の対象者
- サイエンスプログラムを覚えたい方

普段から、サイエンスプログラムを書いているCGエンジニアの方には、見る必要はありません。

### 本題
今回、お話する内容は、極座標系についてです。
式はこれです。

```
x = r sinΘ * cosφ
y = r cosΘ
z = r sinΘ * sinφ
```

r は、中心からの距離。Θ と φ は、それぞれ違う角度になります。
y と z が逆になっている解釈もありますが、私の場合はこれで教えられました。


では、この極座標系が、何に使えるのか？いう事例を紹介させていただきます。

### 正二十面体の作成
sphereを計算によって作成する事ができます。
以下の画像は、正二十面体をプログラムによって作成した実行結果です。

![screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/153021/abfbcee4-5b62-74f8-61ee-069fbc8eb94a.png)

正二十面体である為、正確には、sphereではありませんが、ちょっとしたデバッグ機能でCollider を表示したい時に、sphereのMeshをロードするのは勿体無い気がします。
この疑似sphereは、プログラムで生成している為、頂点数や面数などを減らしたり、Lineだけでの描画にする事も可能です。

実際に式を用いているプログラムは、以下になります。

```
//---------------------------------------------------- 
// 極座標で正N面体を作る
//----------------------------------------------------
#define N = 20

Vertex triangleVertices[N * N * 3];
int herf_n = N / 2;
for (int i = 0; i < N; i++)
{
	for (int j = 0; j < herf_n ; j++)
	{
		float radian = 2.0f * XM_PI / N;
		int index = i * 6 * herf_n + j * 6;
		triangleVertices[index].position.x = SPHERE_RADIUS * sinf(radian * j) * cosf(radian * i);
		triangleVertices[index].position.y = SPHERE_RADIUS * cosf(radian * j);
		triangleVertices[index].position.z = SPHERE_RADIUS * sinf(radian * j) * sinf(radian * i);
		triangleVertices[index].color = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);

		triangleVertices[index+1].position.x = SPHERE_RADIUS * sinf(radian * j) * cosf(radian * (i+1));
		triangleVertices[index+1].position.y = SPHERE_RADIUS * cosf(radian * j);
		triangleVertices[index+1].position.z = SPHERE_RADIUS * sinf(radian * j) * sinf(radian * (i+1));
		triangleVertices[index+1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

		triangleVertices[index+2].position.x = SPHERE_RADIUS * sinf(radian * (j+1)) * cosf(radian * i);
		triangleVertices[index+2].position.y = SPHERE_RADIUS * cosf(radian * (j+1));
		triangleVertices[index+2].position.z = SPHERE_RADIUS * sinf(radian * (j+1)) * sinf(radian * i);
		triangleVertices[index+2].color = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);

		triangleVertices[index+3].position.x = SPHERE_RADIUS * sinf(radian * (j+1)) * cosf(radian * i);
		triangleVertices[index+3].position.y = SPHERE_RADIUS * cosf(radian * (j+1));
		triangleVertices[index+3].position.z = SPHERE_RADIUS * sinf(radian * (j+1)) * sinf(radian * i);
		triangleVertices[index+3].color = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);

		triangleVertices[index+4].position.x = SPHERE_RADIUS * sinf(radian * j) * cosf(radian * (i+1));
		triangleVertices[index+4].position.y = SPHERE_RADIUS * cosf(radian * j);
		triangleVertices[index+4].position.z = SPHERE_RADIUS * sinf(radian * j) * sinf(radian * (i+1));
		triangleVertices[index+4].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);

		triangleVertices[index+5].position.x = SPHERE_RADIUS * sinf(radian * (j+1)) * cosf(radian * (i+1));
		triangleVertices[index+5].position.y = SPHERE_RADIUS * cosf(radian * (j+1));
		triangleVertices[index+5].position.z = SPHERE_RADIUS * sinf(radian * (j+1)) * sinf(radian * (i+1));
		triangleVertices[index+5].color = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
	}
}
```

テストプログラムである為、マクロを使用しています。
スイカの皮のような10個のポリゴンを繋げたものを作成し、それを20個作成する事で実現しています。

もう一つ、事例を紹介します。


### Viewerのようなカメラの動きをする

この式の強みは、ある中心からの距離(r) と 二つの角度(Θとφ)を渡す事で、360度どの位置でも座標を算出する事ができる点です。
想像しやすいのが、360度どこからでもキャラを見ることができる Viewer 機能です。
フリック操作の縦の指の移動量をΘに、横の移動量をφに同期させてやる事で、フリック操作でどの角度からでもキャラを見る事ができるUIを表現できます。
拡大や縮小は、r を操作する事で表現可能です。
注視点の移動は、計算された座標に、ベクトルを足す事で表現できます。

三次元の表現ができる為、カメラの移動だけでなく、キャラクターの移動にも使用する事ができますので、是非、試してゲーム数学を楽しんでください。


私からは以上になります。