こんにちはGLES3の例

QOpenGLExtraFunctions を使ったOpenGL ES 3.0 関数のデモ。

概要

この例では、OpenGL 3.3搭載のデスクトップ・プラットフォームとOpenGL ES 3.0搭載のモバイル/組込みデバイスで同じように動作するアプリケーションで、QOpenGLExtraFunctions 、OpenGL ES 3.0関数をクロスプラットフォームで簡単に使用できることを示します。

この例では、QWidget に依存せず、QWindow の便利なサブクラスであるQOpenGLWindow を使用しています。このサブクラスは、OpenGL でレンダリングされたコンテンツを含むウィンドウを簡単に実装することができます。この意味で、この例はOpenGLウィンドウの例を補完するものであり、便利なサブクラスを使用せずにOpenGLベースのQWindow

Qtロゴシェイプの実装は、Hello GL2 Exampleから含まれています。

OpenGLの使用に関するその他の点では、次のような違いがあります。

  • OpenGLコンテキストの作成は、使用する機能に対して十分に高いバージョン番号を持つ必要があります。
  • シェーダーのバージョン指令が異なる。

main.cppでの設定

ここでは、QGuiApplication 、QSurfaceformat をインスタンス化し、depth buffer size を設定します:

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QSurfaceFormat fmt;
    fmt.setDepthBufferSize(24);

QOpenGLContext::openGLModuleType()に応じて、OpenGL 3.3コアまたはOpenGL ES 3.0コンテキストを要求します:

   if(QOpenGLContext::openGLModuleType()== ::LibGL) { if ( QOpenGLContext::LibGL) { とします。        qDebug("Requesting 3.3 core context");
        fmt.setVersion(3, 3); fmt.setProfile(QSurfaceFormat::CoreProfile); }else{.        qDebug("Requesting 3.0 context");
        fmt.setVersion(3, 0); }.  QSurfaceFormat::setDefaultFormat(fmt);

デフォルトのサーフェスフォーマットを設定し、GLWindowglWindow をインスタンス化します。

GLWindowの実装

このクラスはサンプル・アプリケーションの機能を提供する。

手始めに、QOpenGLWindow のサブクラスを実装して、GLWindow を宣言する:

class GLWindow : public QOpenGLWindow

以下のプロパティは、Q_PROPERTY を使用して宣言されています:

{
    Q_OBJECT
    Q_PROPERTY(float z READ z WRITE setZ)
    Q_PROPERTY(float r READ r WRITE setR)
    Q_PROPERTY(float r2 READ r2 WRITE setR2)

以下のパブリック関数が宣言されている:

public:
    GLWindow();
    ~GLWindow();

    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();

    float z() const { return m_eye.z(); }
    void setZ(float v);

    float r() const { return m_r; }
    void setR(float v);
    float r2() const { return m_r2; }
    void setR2(float v);

以下のプライベート・オブジェクトが宣言されています:

private slots:
    void startSecondStage();
private:
    QOpenGLTexture *m_texture = nullptr;
    QOpenGLShaderProgram *m_program = nullptr;
    QOpenGLBuffer *m_vbo = nullptr;
    QOpenGLVertexArrayObject *m_vao = nullptr;
    Logo m_logo;
    int m_projMatrixLoc = 0;
    int m_camMatrixLoc = 0;
    int m_worldMatrixLoc = 0;
    int m_myMatrixLoc = 0;
    int m_lightPosLoc = 0;
    QMatrix4x4 m_proj;
    QMatrix4x4 m_world;
    QVector3D m_eye;

実装側では、インラインで宣言されていない関数は、glwindow.cpp で実装(または再実装)されます。以下のセレクションでは、OpenGL ES 3.0の使用に関連する実装の特殊性を取り上げます。

アニメーション

以下のコードはアニメーションに関連するもので、ここでは説明しません:

GLWindow::GLWindow()
{
    m_world.setToIdentity();
    m_world.translate(0, 0, -1);
    m_world.rotate(180, 1, 0, 0);

    QSequentialAnimationGroup *animGroup = new QSequentialAnimationGroup(this);
    animGroup->setLoopCount(-1);
    QPropertyAnimation *zAnim0 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
    zAnim0->setStartValue(1.5f);
    zAnim0->setEndValue(10.0f);
    zAnim0->setDuration(2000);
    animGroup->addAnimation(zAnim0);
    QPropertyAnimation *zAnim1 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
    zAnim1->setStartValue(10.0f);
    zAnim1->setEndValue(50.0f);
    zAnim1->setDuration(4000);
    zAnim1->setEasingCurve(QEasingCurve::OutElastic);
    animGroup->addAnimation(zAnim1);
    QPropertyAnimation *zAnim2 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
    zAnim2->setStartValue(50.0f);
    zAnim2->setEndValue(1.5f);
    zAnim2->setDuration(2000);
    animGroup->addAnimation(zAnim2);
    animGroup->start();

    QPropertyAnimation* rAnim = new QPropertyAnimation(this, QByteArrayLiteral("r"));
    rAnim->setStartValue(0.0f);
    rAnim->setEndValue(360.0f);
    rAnim->setDuration(2000);
    rAnim->setLoopCount(-1);
    rAnim->start();

    QTimer::singleShot(4000, this, &GLWindow::startSecondStage);
}

GLWindow::~GLWindow()
{
    makeCurrent();
    delete m_texture;
    delete m_program;
    delete m_vbo;
    delete m_vao;
}

void GLWindow::startSecondStage()
{
    QPropertyAnimation* r2Anim = new QPropertyAnimation(this, QByteArrayLiteral("r2"));
    r2Anim->setStartValue(0.0f);
    r2Anim->setEndValue(360.0f);
    r2Anim->setDuration(20000);
    r2Anim->setLoopCount(-1);
    r2Anim->start();
}

void GLWindow::setZ(float v)
{
    m_eye.setZ(v);
    m_uniformsDirty = true;
    update();
}

void GLWindow::setR(float v)
{
    m_r = v;
    m_uniformsDirty = true;
    update();
}

void GLWindow::setR2(float v)
{
    m_r2 = v;
    m_uniformsDirty = true;
    update();
}

詳しくはQPropertyAnimation,QSequentialAnimationGroup のドキュメントをご覧ください。

シェーダー

シェーダーは次のように定義します:

static const char *vertexShaderSource =
    "layout(location = 0) in vec4 vertex;\n"
    "layout(location = 1) in vec3 normal;\n"
    "out vec3 vert;\n"
    "out vec3 vertNormal;\n"
    "out vec3 color;\n"
    "uniform mat4 projMatrix;\n"
    "uniform mat4 camMatrix;\n"
    "uniform mat4 worldMatrix;\n"
    "uniform mat4 myMatrix;\n"
    "uniform sampler2D sampler;\n"
    "void main() {\n"
    "   ivec2 pos = ivec2(gl_InstanceID % 32, gl_InstanceID / 32);\n"
    "   vec2 t = vec2(float(-16 + pos.x) * 0.8, float(-18 + pos.y) * 0.6);\n"
    "   float val = 2.0 * length(texelFetch(sampler, pos, 0).rgb);\n"
    "   mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, val, 1) * worldMatrix;\n"
    "   color = texelFetch(sampler, pos, 0).rgb * vec3(0.4, 1.0, 0.0);\n"
    "   vert = vec3(wm * vertex);\n"
    "   vertNormal = mat3(transpose(inverse(wm))) * normal;\n"
    "   gl_Position = projMatrix * camMatrix * wm * vertex;\n"
    "}\n";

static const char *fragmentShaderSource =
    "in highp vec3 vert;\n"
    "in highp vec3 vertNormal;\n"
    "in highp vec3 color;\n"
    "out highp vec4 fragColor;\n"
    "uniform highp vec3 lightPos;\n"
    "void main() {\n"
    "   highp vec3 L = normalize(lightPos - vert);\n"
    "   highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
    "   highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
    "   fragColor = vec4(col, 1.0);\n"
    "}\n";

注: これらはOpenGLのバージョンに依存しません。私たちはこれを受け取り、次のようにバージョンを追加します:

QByteArray versionedShaderCode(const char *src)
{
    QByteArray versionedSrc;

    if (QOpenGLContext::currentContext()->isOpenGLES())
        versionedSrc.append(QByteArrayLiteral("#version 300 es\n"));
    else
        versionedSrc.append(QByteArrayLiteral("#version 330\n"));

    versionedSrc.append(src);
    return versionedSrc;
}
OpenGLの初期化

シェーダープログラムの初期化はinitializeGL() で処理される:

void GLWindow::initializeGL()
{
    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();

    QImage img(":/qtlogo.png");
    Q_ASSERT(!img.isNull());
    delete m_texture;
    m_texture = new QOpenGLTexture(img.scaled(32, 36).mirrored());

    delete m_program;
    m_program = new QOpenGLShaderProgram;

これでOpenGLのバージョンがプリペンドされ、さまざまな行列とライトの位置が設定されます:

    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vertexShaderSource));
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fragmentShaderSource));
    m_program->link();

    m_projMatrixLoc = m_program->uniformLocation("projMatrix");
    m_camMatrixLoc = m_program->uniformLocation("camMatrix");
    m_worldMatrixLoc = m_program->uniformLocation("worldMatrix");
    m_myMatrixLoc = m_program->uniformLocation("myMatrix");
    m_lightPosLoc = m_program->uniformLocation("lightPos");

ES 3では厳密には必要ありませんが、頂点配列オブジェクトが作成されます。

    delete m_vao;
    m_vao = new QOpenGLVertexArrayObject;
    if (m_vao->create())
        m_vao->bind();

    m_program->bind();
    delete m_vbo;
    m_vbo = new QOpenGLBuffer;
    m_vbo->create();
    m_vbo->bind();
    m_vbo->allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
    f->glEnableVertexAttribArray(0);
    f->glEnableVertexAttribArray(1);
    f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
                             nullptr);
    f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
                             reinterpret_cast<void *>(3 * sizeof(GLfloat)));
    m_vbo->release();

    f->glEnable(GL_DEPTH_TEST);
    f->glEnable(GL_CULL_FACE);
ウィンドウのサイズ変更

パースペクティブを新しいウィンドウサイズに合わせる必要があります:

void GLWindow::resizeGL(int w, int h)
{
    m_proj.setToIdentity();
    m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
    m_uniformsDirty = true;
}
絵画

GL(ES)2.0が提供する以上のことをしたいので、QOpenGLFunctions の代わりにQOpenGLExtraFunctions を使います:

void GLWindow::paintGL()
{
    // Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to
    // do more than what GL(ES) 2.0 offers.
    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();

スクリーンとバッファをクリアし、シェーダプログラムとテクスチャをバインドします:

    f->glClearColor(0, 0, 0, 1);
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_program->bind();
    m_texture->bind();

最初のpaintGL() 呼び出しや、resizeGL() 呼び出し後の呼び出しを処理するためのロジックは、次のように実装されています:

    if (m_uniformsDirty) {
        m_uniformsDirty = false;
        QMatrix4x4 camera;
        camera.lookAt(m_eye, m_eye + m_target, QVector3D(0, 1, 0));
        m_program->setUniformValue(m_projMatrixLoc, m_proj);
        m_program->setUniformValue(m_camMatrixLoc, camera);
        QMatrix4x4 wm = m_world;
        wm.rotate(m_r, 1, 1, 0);
        m_program->setUniformValue(m_worldMatrixLoc, wm);
        QMatrix4x4 mm;
        mm.setToIdentity();
        mm.rotate(-m_r2, 1, 0, 0);
        m_program->setUniformValue(m_myMatrixLoc, mm);
        m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
    }

最後に、OpenGL 3.1またはOpenGL ES 3.0で導入された関数を示します:

    f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36);
}

これは、先に3.3または3.0のコンテキストを要求したため動作します。

サンプル・プロジェクト @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.