Hello GLES3 예제
QOpenGLExtraFunctions 을 통해 OpenGL ES 3.0 기능을 시연합니다.
개요
이 예제는 OpenGL 3.3이 설치된 데스크톱 플랫폼과 OpenGL ES 3.0이 설치된 모바일/임베디드 기기에서 동일하게 작동하는 애플리케이션에서 QOpenGLExtraFunctions 을 통해 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 컨텍스트를 요청합니다:
만약 (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 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; }
페인팅
QOpenGLFunctions 대신 QOpenGLExtraFunctions 을 사용하는 이유는 GL(ES) 2.0이 제공하는 것 이상의 기능을 제공하고자 하기 때문입니다:
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 컨텍스트를 요청했기 때문에 작동합니다.
© 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.