GLES3 예제 안녕하세요
QOpenGLExtraFunctions 를 통해 OpenGL ES 3.0 함수를 시연합니다.

개요
이 예제는 QOpenGLExtraFunctions 를 통해 OpenGL ES 3.0 함수를 쉽고 크로스 플랫폼으로 사용하는 방법을 보여줍니다. 이 애플리케이션은 OpenGL 3.3을 사용하는 데스크톱 플랫폼과 OpenGL ES 3.0을 사용하는 모바일/임베디드 장치에서 동일하게 작동합니다.
이 예제는 QWidget 에 대한 종속성이 없으며, OpenGL로 렌더링된 콘텐츠를 포함하는 창을 쉽게 구현할 수 있도록 해주는 QWindow 의 편의 서브클래스인 QOpenGLWindow 를 사용합니다. 이러한 측면에서 이 예제는 편의 서브클래스를 사용하지 않고 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 구현
이 클래스는 예제 애플리케이션의 기능을 제공합니다.
시작하기 위해, ` GLWindow `은 ` QOpenGLWindow`의 서브클래스를 구현하여 선언됩니다:
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.