Hallo GLES3 Beispiel
Demonstriert OpenGL ES 3.0 Funktionen über QOpenGLExtraFunctions.
Überblick
Dieses Beispiel demonstriert die einfache, plattformübergreifende Verwendung von OpenGL ES 3.0-Funktionen über QOpenGLExtraFunctions in einer Anwendung, die auf Desktop-Plattformen mit OpenGL 3.3 und mobilen/eingebetteten Geräten mit OpenGL ES 3.0 identisch funktioniert.
Dieses Beispiel hat keine QWidget Abhängigkeiten, sondern verwendet QOpenGLWindow, eine Convenience-Unterklasse von QWindow, die eine einfache Implementierung von Fenstern ermöglicht, die OpenGL-gerenderte Inhalte enthalten. In diesem Sinne ergänzt es das OpenGL Window Example, das die Implementierung eines OpenGL-basierten QWindow ohne Verwendung der Convenience-Subklasse zeigt.
Die Implementierung der Qt-Logoform ist im Hello GL2-Beispiel enthalten.
In anderen Aspekten, die die Verwendung von OpenGL betreffen, gibt es die folgenden Unterschiede.
- Die OpenGL-Kontexterstellung muss eine ausreichend hohe Versionsnummer für die verwendeten Funktionen haben.
- Die Versionsanweisung des Shaders ist anders.
Einrichten in main.cpp
Hier instanziieren wir unser QGuiApplication, QSurfaceformat und setzen dessen depth buffer size:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QSurfaceFormat fmt; fmt.setDepthBufferSize(24);
Wir fordern einen OpenGL 3.3 core oder OpenGL ES 3.0 Kontext an, abhängig von QOpenGLContext::openGLModuleType():
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);
Wir setzen das Standard-Oberflächenformat und instanziieren unser GLWindow glWindow
.
GLWindow implementieren
Diese Klasse stellt die Funktionen der Beispielanwendung bereit.
Zunächst wird GLWindow
deklariert, indem eine Unterklasse von QOpenGLWindow implementiert wird:
class GLWindow : public QOpenGLWindow
Die folgenden Eigenschaften werden mit Q_PROPERTY deklariert:
{ 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)
Die folgenden öffentlichen Funktionen werden deklariert:
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);
Die folgenden privaten Objekte werden deklariert:
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;
Auf der Implementierungsseite werden die Funktionen, die nicht inline deklariert sind, in glwindow.cpp
implementiert (oder reimplementiert). Die folgenden Abschnitte befassen sich mit Implementierungsbesonderheiten im Zusammenhang mit der Verwendung von OpenGL ES 3.0.
Animationen
Der folgende Code bezieht sich auf die Animationen und wird hier nicht näher erläutert:
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(); }
Weitere Informationen finden Sie in der Dokumentation zu QPropertyAnimation, QSequentialAnimationGroup.
Shader
Die Shader werden wie folgt definiert:
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";
Hinweis: Diese sind unabhängig von der OpenGL-Version. Wir nehmen dies und fügen die Version wie folgt an:
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; }
Initialisierung von OpenGL
Die Initialisierung des Shader-Programms wird von initializeGL()
übernommen:
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;
Jetzt wird die OpenGL-Version vorangestellt und die verschiedenen Matrizen und Lichtpositionen werden eingestellt:
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");
Obwohl für ES 3 nicht unbedingt erforderlich, wird ein Vertex-Array-Objekt erstellt.
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);
Größenänderung des Fensters
Die Perspektive muss an die neue Fenstergröße angepasst werden:
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; }
Malen
Wir verwenden QOpenGLExtraFunctions anstelle von QOpenGLFunctions, da wir mehr tun wollen als das, was GL(ES) 2.0 bietet:
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();
Wir löschen den Bildschirm und die Puffer und binden unser Shader-Programm und die Textur:
f->glClearColor(0, 0, 0, 1); f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_program->bind(); m_texture->bind();
Die Logik für die Behandlung eines anfänglichen paintGL()
-Aufrufs oder eines Aufrufs nach einem resizeGL()
-Aufruf ist wie folgt implementiert:
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)); }
Zuletzt demonstrieren wir eine Funktion, die in OpenGL 3.1 oder OpenGL ES 3.0 eingeführt wurde:
f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36); }
Dies funktioniert, weil wir zuvor 3.3 oder 3.0 Kontext angefordert haben.
© 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.