GLES3の「Hello」サンプル
QOpenGLExtraFunctions を通じて OpenGL ES 3.0 の機能を実演します。

概要
このサンプルでは、QOpenGLExtraFunctions を使用して、OpenGL 3.3 に対応したデスクトッププラットフォームと OpenGL ES 3.0 に対応したモバイル/組み込みデバイスで、同じ動作をするアプリケーションにおいて、OpenGL ES 3.0 関数を簡単にクロスプラットフォームで利用する方法を実演します。
このサンプルにはQWidget への依存関係はなく、QOpenGLWindow を使用しています。これはQWindow の利便性を高めるサブクラスであり、OpenGLでレンダリングされたコンテンツを含むウィンドウを簡単に実装できます。この点において、このサンプルは、利便性を高めるサブクラスを使用せずにOpenGLベースのQWindow を実装する方法を示す「OpenGLウィンドウのサンプル」を補完するものです。
Qtロゴの形状の実装は、「Hello GL2」サンプルから採用されています。
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()==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);デフォルトのサーフェス形式を設定し、GLWindowをインスタンス化しますglWindow 。
GLWindowの実装
このクラスは、サンプルアプリケーションの機能を提供します。
まず、QOpenGLWindow のサブクラスを実装して、GLWindow を宣言します:
class GLWindow : public QOpenGLWindowQ_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).flipped());
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のコンテキストを要求していたためです。
© 2026 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.