2

私は現在、風変わりなレトロ フライト シミュレーションに取り組んでおり、3D プロジェクトでいくつかの問題に遭遇しました。

次の情報が与えられた場合、ゲーム ワールド内の単純な位置ベクトルを画面上の 2D ベクトルに変換するにはどうすればよいですか。

カメラの位置 カメラの向き (以下を参照) 視野 スクリーンの高さと幅 (および縦横比)

向きを保存する方法も探しています。基本的なベクター ライブラリは既に作成していますが、カメラ (および投影コード) と回転の実際の処理の両方で使用する回転を保存する方法がわかりません。ゲーム内オブジェクトの。現在、四元数の使用を検討していますが、射影変換に行列の代わりに四元数を使用することは可能ですか (そして簡単ですか)?

コードでの四元数の実装に関する適切な情報源はありますか? 複素数用に別のライブラリを作成する必要がありますか?

お時間とご協力いただきありがとうございます:)

4

1 に答える 1

8

注意:長い答え!

私は Love2D で同様のプロジェクトを実行しましたが、完全に高速に実行されるため、OpenGL (とにかく公開されていない) を使用するのではなく、Lua で自分で計算を行うことに問題はありません。

コメントに反して、がっかりしないでください。3D の向きと視点の背後にある計算は、実際には非常に単純です。

オリエンテーションの場合、クォータニオンはおそらくやり過ぎです。回転を伴う 3D プロジェクションを行うにはVec2、 、Vec3、およびCameraクラスのみが必要であることがわかりました。数学的には微妙な違いがいくつかありますが、実際にはベクトルのベクトルは完全に適切な変換行列を作成し、変換行列は完全に適切な向きを作成します。ベクトルのベクトルである行列には、両方を処理するために 1 つのクラスを記述するだけでよいという利点があります。

ベクトルを投影するvには、カメラの 3 つのパラメーターを考慮します。

  • locVec3カメラの位置のa
  • trans、カメラの向きの の a ( of とMat3by3も呼ばれる)Vec3Vec3
    • (免責事項:小さな丸め誤差が蓄積される可能性があるため、カメラの向きに行列を使用することは技術的に有害であると考えられていますが、実際の使用では問題ありません)
  • zoom、遠近法を決定するために使用される倍率。のz(カメラに相対的な) はzoom2D と同等です。つまり、パースペクティブからのスケーリングはありません。

投影は次のように機能します。

function Camera:project(v)
    local relv -- v positioned relative to the camera, both in orientation and location
    relv = self.trans * (v - self.loc) -- here '*' is vector dot product
    if relv.z > 0 then
        -- v is in front of the camera
        local w -- perspective scaling factor
        w = self.zoom / relv.z
        local projv -- projected vector
        projv = Vec2(relv.x * w, relv.y * w)
        return projv
    else
        -- v is behind the camera
        return nil
    end
end

これは、Vec2(0, 0)がウィンドウの隅ではなく中央に対応すると仮定しています。その設定は簡単な翻訳です。

trans恒等行列として開始する必要がVec3(Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1))あります。方向が変更されるたびに微調整を行いながら、段階的に計算されます。

マトリックスの基本を既に知っていると思いますが、そうでない場合の考え方は次のとおりです。マトリックスはベクトルのベクトルであり、少なくともこの場合は座標系と考えることができます。各ベクトルは、座標系の 1 つの軸と考えることができます。マトリックスの要素 (ベクトルであり、マトリックスの列と見なされます) を変更することにより、その座標系における座標の意味を変更します。通常の使用法では、ベクトルの最初の要素は右に移動することを意味し、2 番目の要素は上に移動することを意味し、3 番目の要素は前方に移動することを意味します。ただし、マトリックスを使用すると、各コンポーネントを任意の方向に向けることができます。内積の定義は

function Vec3.dot(a, b) return a.x * b.x + a.y + b.y + a.z * b.z end

行列の場合

Vec3(axis1, axis2, axis3)

内積の定義が与えられると、ベクトルで点を打った行列は次のようになりますv

axis1 * v.x + axis2 * v.y + axis3 * v.z

つまり、 の最初の要素はの移動数を表し、2 番目の要素はの移動数を表し、3 番目の要素はの移動数を表し、表現された場合の最終結果は になりvます。行列の座標ではなく、標準座標で。行列にベクトルを掛けると、ベクトルの成分の意味が変わります。本質的に、それは「右にあったものはすべて、右に少なくなり、より前にある」などのステートメントの数学的表現です。ある文では、行列が空間を変換します。axis1axis2axis3v

目の前のタスクに戻ると、「ピッチ」(x 軸の周りを意味する) の回転thetaを行列を使用して角度で表すには、次のように記述できます。

function pitchrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        -- we're rotating *around* the x axis, so it stays the same
        Vec3(
            1,
            0,
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            0,
            math.cos(theta),
            math.sin(theta)
        ),
        -- axis 3
        -- rotated z axis
        Vec3(
            0,
            -math.sin(theta),
            math.cos(theta)
        )
    )
end

「ヨー」(y軸周り)の場合:

function yawrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        Vec3(
            math.cos(theta),
            0,
            math.sin(theta)
        ),
        -- axis 2
        -- rotated y axis
        -- we're rotating *around* the y axis, so it stays the same
        Vec3(
            0,
            1,
            0
        ),
        -- axis 3
        -- rotated z axis
        Vec3(
            -math.sin(theta),
            0,
            math.cos(theta)
        )
    )
end

最後に、(z 軸を中心に) 「ロール」します。これは、フライト シミュレーションで特に役立ちます。

function rollrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        Vec3(
            math.cos(theta),
            math.sin(theta),
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            -math.sin(theta),
            math.cos(theta),
            0
        ),
        -- axis 3
        -- rotated z axis
        -- we're rotating *around* the z axis, so it stays the same
        Vec3(
            0,
            0,
            1
        )
    )
end

それが x、y、z 軸に与える影響を頭の中で視覚化すると、これらすべてのコサインとサイン、および符号反転が意味をなすようになるかもしれません。彼らはすべて理由があってそこにいます。

最後に、これらの回転を適用するパズルの最後のステップに到達します。行列の優れた機能は、それらを簡単に合成できることです。変換は非常に簡単に変換できます。各軸を変換するだけです。A既存の行列を行列で変換するにはB:

function combinematrices(a, b)
    return Vec3(b * a.x, b * a.y, b * a.z) -- x y and z are the first second and third axes
end

これが意味することは、カメラに変更を適用したい場合は、このマトリックス結合メカニズムを使用して、フレームごとに向きを少し回転させるだけでよいということです。カメラ クラスのこれらの関数を使用すると、変更を簡単に行うことができます。

function Camera:rotateyaw(theta)
    self.trans = combinematrices(self.trans, yawrotation(-theta))
end

射影のために、トランスをカメラの向きの反対にしたいので、負のシータを使用します。同様の機能をピッチとロールで作成できます。

これらの構成要素がすべて整ったら、Lua で 3D グラフィックス コードを作成する準備が整います。あなたは実験したいと思うでしょうzoom- 私は通常 を使用します500が、それは実際にはアプリケーションに依存します.

OpenGL なしでは実現できない 1 つの欠けている部分は、深度テストです。ワイヤフレームのポイント以外を描画している場合、すべてが正しい順序で描画されることを確認する良い方法はありません。並べ替えはできますが、それは非効率的です。また、OpenGL が行うように、ピクセルごとに並べ替える必要があるまれなケースを処理できません。

ハッピーコーディング!お役に立てば幸いです。

于 2013-01-02T11:57:36.530 に答える