ゲーム数学の極座標系


この記事は、数学ゲーム 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 を操作する事で表現可能です。
注視点の移動は、計算された座標に、ベクトルを足す事で表現できます。

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

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