あなたの質問は正しくありません。「何かを回転すると、軸は同じ (x,y) ではなくなるので、どの軸でどれだけ回転するかをどうやって調べることができますか?」と尋ねないでください。軸?」グローバル x を中心とした回転は、オブジェクトのローカル x 軸を中心とした回転ではなく、常にグローバル x を中心とした回転である必要があります。現在の状態に応じて異なる方法で回転するのではなく、概念的に回転しているものを変更するだけです。
まず第一に、おそらく rotX、rotY、rotZ を格納し、glRotatef を呼び出して回転を適用しますか? それはあなたがやりたいことには十分ではありません。あなたがやっていることは、オイラー角による向きを記述していることです。常にジンバル ロックが発生します。これは、軸の 1 つの値によって他の 2 つの軸が同じ軸に変わる場所です。そのため、自由度が失われます。適切な説明については、記事を参照してください。
OpenGL コーダーとしては非常に満足できるので、代わりに、オブジェクトの向きを M という行列として直接格納していたとします。回転を実現するために適用する行列として、回転を格納します。角度や軸の明確な概念はありません。
x を中心に回転させたいとします。x 軸を中心に適切な回転行列を生成し、それを R と呼びます。このような行列は、OpenGL が glRotatef(angle, 1, 0, 0) を実行するために内部的に構築するものです。
次に、実際には 2 つのオプションがあります。M を MR に置き換えるか、M を RM に置き換えます。行列の乗算は可換ではないため、それらは同じではありません。OpenGL マトリックス スタックを使用して 6 を左に移動してから 40 度回転すると、40 度回転してから 6 を左に移動する場合とは異なる結果になります。
この場合、R は目的語なので、MR は後乗算で、RM は前乗算です。行列の乗算は、ベクトルに適用されると、2 番目の行列を適用してから最初の行列を適用するのと同じ効果を持つ行列を作成します。したがって、後置乗算すると、あたかも新しい回転でオブジェクトを回転させ、その後に先行するすべての回転を行ったかのような結果が得られます。それは明らかに間違った方法です。現在までの他のすべての変更の後に、新しい変更を有効にする必要があります。したがって、事前乗算します。そして、結果を新しい M として保存します。
実際にはそれだけです。次のように、GL マトリックス スタックを明示的に使用して、概念実証をすばやく行うことができます。
// ... I assume the modelview stack is active, M is an array of 16 floats ...
// ensure whatever's on the stack currently is kept safe
glPushMatrix();
// but we don't actually care what it was, so load the identity
glLoadIdentity();
// build our rotation matrix first
glRotatef(changeAroundX, 1, 0, 0);
glRotatef(changeAroundY, 0, 1, 0);
// multiply by M. OpenGL postmultiplies by the newer matrix, so this
// is premultiplying M by whatever we just loaded
glMultMatrixf(M);
// read the new M back
glGetFloatv(GL_MODELVIEW_MATRIX, M);
// and pretend we never touched the stack
glPopMatrix();
次に、glRotatef の代わりに glMultMatrixf(M) を使用して、現在のオブジェクトの回転を適用します。これは概念の証明にすぎません。これは、M に対して大量の操作を行った結果として、M で厄介な数値エラーがすぐに発生するためです。進行中に M を修正できますが、既にクォータニオン コードがあり、それに満足している場合は、そのルートをたどる方が賢明かもしれません。論理行列演算を同等の四元数に置き換えるだけです。