これは複雑なことです。このトピックに関する本を読んで、すべての数学と核心を理解してください。このようなもので長く遊ぶ予定がある場合は、これらのことを知っておく必要があります. この答えは、足を濡らしてハッキングできるようにするためのものです。
乗算行列
まず最初に。行列の乗算はかなり単純な作業です。
AB = Cである行列A、B、およびCがあるとします。行列Cの行 3、列 2の値を求めたいとしましょう。
- Aの 3 行目とBの 2 列目を取得します。これで、 AとBから同じ数の値が得られるはずです。(これらの 2 つの行列に対して行列の乗算が定義されていない場合、実行できません。) 両方とも 4×4 行列の場合、A (行 3) からの 4 つの値とB (行 3) からの 4 つの値が必要です(列 2)。
- A の各値にBの各値を掛けます。4 つの新しい値で終了する必要があります。
- これらの値を追加します。
これで行列Cの値が行 3、列 2 に表示されました。もちろん、課題はこれをプログラムで行うことです。
/* AB = C
Row-major ordering
a[0][0] a[0][2] a[0][3]...
a[1][0] a[1][4] ...
a[2][0] ...
...*/
public static mmMul(double[][] a, double[][] b, double[][] c) {
c_height = b.length; // Height of b
c_width = a[0].length; // Width of a
common_side = a.length; // Height of a, width of b
for (int i = 0; i < c_height; i++) {
for (int j = 0; j < c_width; j++) {
// Ready to calculate value of c[i][j]
c[i][j] = 0;
// Iterate through ith row of a, jth col of b in lockstep
for (int k = 0; k < common_side; k++) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
}
同次座標
3D座標があります。(5, 2, 1) があるとしましょう。これらはデカルト座標です。それらを ( x、y、z )と呼びましょう。
同次座標とは、デカルト座標の最後に余分な 1 を書き込むことを意味します。(5, 2, 1) は (5, 2, 1, 1) になります。それらを ( x、y、z、w )と呼びましょう。
w ≠ 1 になる変換を行うときはいつでも、座標のすべてのコンポーネントをwで除算します。これにより、x、y、およびzが変更され、w = 1 に戻ります。(変換がwに変わらない場合でも、これを実行しても害はありません。すべてを 1 で除算するだけで、何もしません。)
それらの背後にある数学が完全に意味をなさない場合でも、同次座標で実行できるいくつかの非常に優れた機能があります。この時点で、この回答の上部にあるアドバイスをもう一度見てください。
ポイントの変換
このセクションと次のセクションでは、OpenGL の用語とアプローチを使用します。不明な点や目標と矛盾する点がある場合 (これは漠然と宿題のように思えるため :P)、コメントを残してください。
また、ロール、チルト、およびパンのマトリックスが正しいと仮定することから始めます。
変換行列を使用してポイントを変換する場合は、その行列にポイントを表す列ベクトルを右乗算します。(5, 2, 1) を変換行列Aで変換したいとします。最初にv = [5, 2, 1, 1] Tを定義します。([ x , y , z , w ] Tを小さな Tで書き、列ベクトルとして記述する必要があることを意味します。)
// Your point in 3D
double v[4][5] = {{5}, {2}, {1}, {1}}
この場合、Av = v 1で、v 1は変換されたポイントです。この乗算は、 Aが 4×4 でvが 4×1の行列乗算のように行います。最終的に 4×1 の行列 (別の列ベクトル) になります。
// Transforming a single point with a roll
double v_1[4][6];
mmMul(rollMat, v, v_1);
ここで、適用する変換行列が複数ある場合は、まずそれらを 1 つの変換行列に結合します。これを行うには、適用する順序で行列を乗算します。
プログラムでは、単位行列から始めて、各変換行列を右乗算する必要があります。I 4を4×4 単位行列とし、A 1、A 2、A 3、... を変換行列とします。最終的な変換行列をA finalにします
Aファイナル← I 4
Aファイナル← Aファイナル A 1
Aファイナル← Aファイナル A 2
Aファイナル← Aファイナル A 3
割り当てを表すためにその矢印を使用していることに注意してください。これを実装するときは、行列の乗算計算で使用している間にA final を上書きしないように注意してください。コピーを作成します。
// A composite transformation matrix (roll, then tilt)
double a_final[4][4] =
{
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
}; // the 4 x 4 identity matrix
double a_final_copy[4][4];
mCopy(a_final, a_final_copy); // make a copy of a_final
mmMul(rollMat, a_final_copy, a_final);
mCopy(a_final, a_final_copy); // update the copy
mmMul(tiltMat, a_final_copy, a_final);
最後に、上記と同じ乗算を行います: A final v = v 1
// Use the above matrix to transform v
mmMul(a_final, v, v_1);
最初から最後まで
カメラの変換は、ビュー マトリックスとして表す必要があります。ここでAビュー v = v 1操作を実行します。( vは世界座標を 4×1 の列ベクトルとして表します。A finalはAビューです。)
// World coordinates to eye coordinates
// A_view is a_final from above
mmMult(a_view, v_world, v_view);
射影変換は、透視変換を表します。これにより、近くのオブジェクトが大きくなり、遠くのオブジェクトが小さくなります。これは、カメラの変換後に実行されます。パースペクティブがまだ必要ない場合は、射影行列に単位行列を使用してください。とにかく、ここでA v 1 = v 2を実行します。
// Eye coordinates to clip coordinates
// If you don't care about perspective, SKIP THIS STEP
mmMult(a_projection, v_view, v_eye);
次に、パースペクティブ分割を行う必要があります。ここでは、まだ説明していない同次座標について詳しく説明します。とにかく、 v 2 のすべてのコンポーネントをv 2の最後のコンポーネントで割ります。v 2 = [ x , y , z , w ] Tの場合、各成分をw ( w自体を含む)で除算します。最終的にw = 1 になるはずです (前述のように射影行列が恒等行列の場合、この手順では何も実行されません)。
// Clip coordinates to normalized device coordinates
// If you skipped the previous step, SKIP THIS STEP
for (int i = 0; i < 4; i++) {
v_ndc[i] = v_eye[i] / v[3];
}
最後に、あなたのv 2を取ります。最初の 2 つの座標は、x座標とy座標です。3 番目はzで、これは破棄できます。(後で、非常に高度になったら、このz値を使用して、他の点の前または後ろにある点を把握できます。) この時点で、最後のコンポーネントはw = 1 であるため、必要はありません。それはもうまったく。
x = v_ndc[0]
y = v_ndc[1]
z = v_ndc[2] // unused; your screen is 2D
遠近法と遠近法の分割の手順をスキップした場合は、上記v_view
の代わりに使用しv_ndc
ます。
これは、 OpenGL 座標系のセットに非常に似ています。違いは、ワールド座標から開始するのに対し、OpenGL はオブジェクト座標から開始することです。違いは次のとおりです。
- 世界座標から始めます
- ビュー マトリックスを使用して、ワールド座標を目の座標に変換します
- OpenGL は ModelView マトリックスを使用してオブジェクト座標を目の座標に変換します
そこから先は、すべて同じです。