【保存版】Qt GUIにおける3Dプログラミングのヒント集 - QMatrix4x4::perspective()編


QMatrix4x4::perspective() は、3D 空間におけるパースペクティブ投影を定義する 4x4 変換行列を生成する Qt GUI の関数です。これは、3D シーンを 2D 画面に投影する際に使用されます。

パラメータ

  • farPlane: 視点から遠いクリップ平面までの距離
  • nearPlane: 視点に近いクリップ平面までの距離
  • aspectRatio: 画面の縦横比 (幅 / 高さ)
  • verticalAngle: 垂直方向の視野角 (度単位)

動作

QMatrix4x4::perspective() は、視錐台を定義する 6 つの平面を指定します。これらの平面は、カメラの位置と方向、およびクリップ平面の距離によって決定されます。

視錐台は、カメラに向かって細くなる逆台形です。近接平面はカメラに近いほど大きく、遠隔平面はカメラから離れるほど小さくなります。

QMatrix4x4::perspective() 関数は、この視錐台を 2D 画面に投影する変換行列を生成します。この行列は、3D 空間内の点を変換し、2D 画面上の対応するピクセル位置を計算するために使用されます。

QMatrix4x4 projectionMatrix;
projectionMatrix.perspective(60.0f, aspectRatio, 0.1f, 100.0f);

// ...

QShaderProgram shaderProgram;
shaderProgram.setUniformValue("projectionMatrix", projectionMatrix);

この例では、projectionMatrix 変数にパースペクティブ投影を定義する 4x4 変換行列が生成されます。この行列は、シェーダー プログラムにユニフォームとして設定され、3D 空間内の点を変換するために使用されます。

  • 画面の縦横比は、実際に使用している画面の比率に一致する必要があります。
  • 垂直方向の視野角は、人間の視野角をシミュレートするために一般的に 60 度程度に設定されます。
  • クリップ平面の距離が小さすぎると、オブジェクトが近すぎて画面に表示されません。逆に、クリップ平面の距離が大きすぎると、オブジェクトが遠すぎて画面に表示されません。
  • QMatrix4x4::perspective() は、OpenGL の gluPerspective() 関数と同様の機能を提供します。


#include <QCoreApplication>
#include <QGLWidget>

class CubeWidget : public QGLWidget {
public:
    CubeWidget() {
        setWindowTitle("Qt Perspective Projection");
        setFixedSize(400, 300);
    }

protected:
    void initializeGL() override {
        glEnable(GL_DEPTH_TEST);
        glShadeModel(GL_SMOOTH);
    }

    void resizeGL(int width, int height) override {
        glViewport(0, 0, width, height);

        // パースペクティブ投影を設定
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f, (float) width / (float) height, 0.1f, 100.0f);

        // モデルビュー変換を設定
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }

    void paintGL() override {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 立方体を回転させる
        glRotatef(rotationAngle, 1.0f, 1.0f, 1.0f);

        // 立方体を描画
        glBegin(GL_QUADS);
            // 前面
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(-1.0f, -1.0f, 1.0f);
            glVertex3f(1.0f, -1.0f, 1.0f);
            glVertex3f(1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);

            // 背面
            glColor3f(0.0f, 1.0f, 0.0f);
            glVertex3f(-1.0f, -1.0f, -1.0f);
            glVertex3f(-1.0f, 1.0f, -1.0f);
            glVertex3f(1.0f, 1.0f, -1.0f);
            glVertex3f(1.0f, -1.0f, -1.0f);

            // 上面
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, -1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f(1.0f, 1.0f, 1.0f);
            glVertex3f(1.0f, 1.0f, -1.0f);

            // 下面
            glColor3f(1.0f, 1.0f, 0.0f);
            glVertex3f(-1.0f, -1.0f, -1.0f);
            glVertex3f(1.0f, -1.0f, -1.0f);
            glVertex3f(1.0f, -1.0f, 1.0f);
            glVertex3f(-1.0f, -1.0f, 1.0f);

            // 左面
            glColor3f(0.5f, 0.5f, 0.5f);
            glVertex3f(-1.0f, -1.0f, -1.0f);
            glVertex3f(-1.0f, -1.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, 1.0f);
            glVertex3f(-1.0f, 1.0f, -1.0f);

            // 右面
            glColor3f(0.7f, 0.7f, 0.7f);
            glVertex3f(1.0f, -1.0f, -1.0f);
            glVertex3f(1.0f, 1.0f, -1.0f);
            glVertex3f(1.0f,


OpenGL 関数を使用する

  • 短所:
    • Qt 固有ではなく、OpenGL の知識が必要
    • コードが煩雑になる可能性がある
  • 長所:
    • 高いパフォーマンス
    • より多くの制御が可能
gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f);

GLSL シェーダーを使用する

  • 短所:
    • シェーダー プログラミングの知識が必要
    • パフォーマンスが低くなる可能性がある
  • 長所:
    • より柔軟な制御が可能
    • 他のシェーダーと組み合わせることができる
uniform mat4 projectionMatrix;

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

カスタム変換行列を構築する

  • 短所:
    • 複雑で計算量が多い
    • バグが発生しやすい
  • 長所:
    • 完全な制御が可能
    • 数学的な知識を活かせる
QMatrix4x4 projectionMatrix;

projectionMatrix.setToIdentity();
projectionMatrix.frustum(-aspectRatio, aspectRatio, -nearPlane, -farPlane);

ライブラリを使用する

  • 短所:
    • 適切なライブラリを見つける必要がある
    • ライブラリのライセンス条項を確認する必要がある
  • 長所:
    • 開発時間を短縮できる
    • テスト済みのコードを使用できる
  • 使いやすさ: 使いやすさを重視する場合は、ライブラリを使用するのが良いでしょう。
  • 制御: 完全な制御が必要な場合は、カスタム変換行列を使用するのが良いでしょう。
  • 柔軟性: 柔軟性が必要な場合は、GLSL シェーダーを使用するのが良いでしょう。
  • パフォーマンス: パフォーマンスが重要な場合は、OpenGL 関数またはカスタム変換行列を使用するのが良いでしょう。