この記事は、数学ゲーム Advent Calendar 2018 の6日目の記事です。
今年のCEDECのこちらの公演がきっかけで、最近、DirectXを勉強し直している、ゲーム会社に勤めるエンジニアです。
DirectX 12を粛々と進めていたところ、ゲーム数学 Advent Calendarが空いているとの事を知り、やっていた事の振り返りも兼ねて、記事を投稿させていただきます。
この記事の対象者
- サイエンスプログラムを覚えたい方
普段から、サイエンスプログラムを書いているCGエンジニアの方には、見る必要はありません。
本題
今回、お話する内容は、極座標系についてです。
式はこれです。
x = r sinΘ * cosφ
y = r cosΘ
z = r sinΘ * sinφ
r は、中心からの距離。Θ と φ は、それぞれ違う角度になります。
y と z が逆になっている解釈もありますが、私の場合はこれで教えられました。
では、この極座標系が、何に使えるのか?いう事例を紹介させていただきます。
正二十面体の作成
sphereを計算によって作成する事ができます。
以下の画像は、正二十面体をプログラムによって作成した実行結果です。
正二十面体である為、正確には、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 を操作する事で表現可能です。
注視点の移動は、計算された座標に、ベクトルを足す事で表現できます。
三次元の表現ができる為、カメラの移動だけでなく、キャラクターの移動にも使用する事ができますので、是非、試してゲーム数学を楽しんでください。
私からは以上になります。