OpenGL 창 예제
이 예는 OpenGL을 사용하기 위한 최소한의 QWindow 기반 애플리케이션을 만드는 방법을 보여줍니다.
참고: 이 예는 OpenGL에서 QWindow 을 사용하는 방법에 대한 낮은 수준의 예입니다. 실제로는 상위 수준인 QOpenGLWindow 클래스를 사용하는 것을 고려해야 합니다. QOpenGLWindow 편의 클래스의 데모는 Hello GLES3 예제를 참조하세요.
OpenGLWindow 슈퍼 클래스
OpenGLWindow 클래스는 실제 렌더링을 수행하도록 서브클래싱된 API 역할을 합니다. 이 클래스에는 렌더링() 호출을 요청하는 함수가 있는데, 렌더링() 즉시 또는 이벤트 루프가 현재 이벤트 일괄 처리를 마치자마자 렌더링() 호출을 요청하는 함수는 렌더러터()를 사용합니다.QPainter OpenGLWindow 서브클래스는 OpenGL 기반 렌더링을 위해 render()를 다시 구현하거나 QPainter. 기본 OpenGL 드라이버에서 수직 동기화가 활성화되어 있다고 가정할 때 수직 재생률로 render()를 호출하려면 OpenGLWindow::setAnimating(true)을 사용합니다.
OpenGL 렌더링을 수행하는 클래스에서는 일반적으로 OpenGL ES 2.0 함수에 대한 플랫폼 독립적인 액세스를 얻기 위해 OpenGLWindow와 같이 QOpenGLFunctions 에서 상속하는 것이 좋습니다. QOpenGLFunctions 에서 상속하면 여기에 포함된 OpenGL 함수가 우선권을 가지며, 애플리케이션이 OpenGL 및 OpenGL ES 2.0에서 작동하도록 하려는 경우 해당 함수를 해결하는 것에 대해 걱정할 필요가 없습니다.
class OpenGLWindow : public QWindow, protected QOpenGLFunctions { Q_OBJECT public: explicit OpenGLWindow(QWindow *parent = nullptr); ~OpenGLWindow(); virtual void render(QPainter *painter); virtual void render(); virtual void initialize(); void setAnimating(bool animating); public slots: void renderLater(); void renderNow(); protected: bool event(QEvent *event) override; void exposeEvent(QExposeEvent *event) override; private: bool m_animating = false; QOpenGLContext *m_context = nullptr; QOpenGLPaintDevice *m_device = nullptr; };
QBackingStore창의 표면 유형을 QSurface::OpenGLSurface 로 설정하여 창이 QPainter 을 사용하여 래스터 콘텐츠를 렌더링하는 것이 아니라 OpenGL 렌더링에 사용됨을 나타내야 합니다.
OpenGLWindow::OpenGLWindow(QWindow *parent) : QWindow(parent) { setSurfaceType(QWindow::OpenGLSurface); }
렌더링()을 처음 호출하기 전에 한 번 호출되는 초기화() 함수를 유효한 현재 QOpenGLContext 로 오버라이드하여 필요한 모든 OpenGL 초기화를 수행할 수 있습니다. 다음 코드 조각에서 볼 수 있듯이 기본 render(QPainter *) 및 initialize() 구현은 비어 있는 반면, 기본 render() 구현은 QOpenGLPaintDevice 을 초기화한 다음 render(QPainter *)로 호출합니다.
void OpenGLWindow::render(QPainter *painter) { Q_UNUSED(painter); } void OpenGLWindow::initialize() { } void OpenGLWindow::render() { if (!m_device) m_device = new QOpenGLPaintDevice; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); m_device->setSize(size() * devicePixelRatio()); m_device->setDevicePixelRatio(devicePixelRatio()); QPainter painter(m_device); render(&painter); }
renderLater() 함수는 QWindow::requestUpdate()를 호출하여 시스템이 다시 칠할 준비가 되면 업데이트를 예약합니다.
또한 노출 이벤트가 발생하면 renderNow()를 호출합니다. 노출 이벤트()는 화면의 노출, 즉 가시성이 변경되었음을 창에 알리는 알림입니다. 노출 이벤트가 수신되면 QWindow::isExposed()를 쿼리하여 창이 현재 노출되었는지 여부를 확인할 수 있습니다. 첫 번째 노출 이벤트를 받기 전에 창에 렌더링하거나 QOpenGLContext::swapBuffers()를 호출하지 마세요. 그 전에는 최종 크기를 알 수 없고 렌더링된 내용이 화면에 표시되지 않을 수도 있기 때문입니다.
void OpenGLWindow::renderLater() { requestUpdate(); } bool OpenGLWindow::event(QEvent *event) { switch (event->type()) { case QEvent::UpdateRequest: renderNow(); return true; default: return QWindow::event(event); } } void OpenGLWindow::exposeEvent(QExposeEvent *event) { Q_UNUSED(event); if (isExposed()) renderNow(); }
renderNow()에서는 현재 노출되지 않은 경우 반환되며, 이 경우 실제로 노출 이벤트를 받을 때까지 렌더링이 지연됩니다. 아직 그렇게 하지 않았다면 OpenGLWindow에 설정된 것과 동일한 QSurfaceFormat 으로 QOpenGLContext 을 생성하고 하위 클래스를 위해 initialize()를 호출하고 QOpenGLFunctions 슈퍼 클래스가 올바른 QOpenGLContext 과 연결되도록 initializeOpenGLFunctions()를 호출합니다. 어쨌든 QOpenGLContext::makeCurrent()를 호출하여 컨텍스트를 최신 상태로 만들고, render ()를 호출하여 실제 렌더링을 수행한 다음, 마지막으로 OpenGLWindow를 매개변수로 사용하여 QOpenGLContext::swapBuffers()를 호출하여 렌더링된 내용을 표시하도록 예약합니다.
렌더링할 표면을 매개변수로 지정하여 QOpenGLContext::makeCurrent()를 호출하여 OpenGL 컨텍스트를 사용한 프레임 렌더링이 시작되면 OpenGL 명령을 실행할 수 있습니다. 이 명령은 시스템의 OpenGL 헤더를 포함하는 <qopengl.h>를 포함하여 직접 실행하거나, 편의를 위해 QOpenGLFunctions 에서 상속하거나 QOpenGLContext::functions()를 사용하여 액세스할 수 있습니다. QOpenGLFunctions 는 OpenGL ES 2.0 및 데스크톱 OpenGL에서 아직 표준이 아닌 모든 OpenGL ES 2.0 수준의 OpenGL 호출에 액세스할 수 있도록 해줍니다. OpenGL 및 OpenGL ES API에 대한 자세한 내용은 공식 OpenGL 레지스트리 및 Khronos OpenGL ES API 레지스트리를 참조하세요.
OpenGLWindow::setAnimating(true)으로 애니메이션이 활성화된 경우 renderLater()를 호출하여 다른 업데이트 요청을 예약합니다.
void OpenGLWindow::renderNow() { if (!isExposed()) return; bool needsInitialize = false; if (!m_context) { m_context = new QOpenGLContext(this); m_context->setFormat(requestedFormat()); m_context->create(); needsInitialize = true; } m_context->makeCurrent(this); if (needsInitialize) { initializeOpenGLFunctions(); initialize(); } render(); m_context->swapBuffers(this); if (m_animating) renderLater(); }
애니메이션을 활성화하면 다음 코드 스니펫과 같이 업데이트 요청도 예약됩니다.
void OpenGLWindow::setAnimating(bool animating) { m_animating = animating; if (animating) renderLater(); }
OpenGL 렌더링 서브 클래스 예제
여기서는 회전하는 삼각형을 렌더링하기 위해 OpenGL을 수행하는 방법을 보여주기 위해 OpenGLWindow를 서브 클래싱합니다. QOpenGLFunctions 을 간접적으로 서브 클래싱하면 모든 OpenGL ES 2.0 수준의 기능에 액세스할 수 있습니다.
class TriangleWindow : public OpenGLWindow { public: using OpenGLWindow::OpenGLWindow; void initialize() override; void render() override; private: GLint m_matrixUniform = 0; QOpenGLBuffer m_vbo; QOpenGLShaderProgram *m_program = nullptr; int m_frame = 0; };
메인 함수에서 QGuiApplication 을 초기화하고 TriangleOpenGLWindow를 인스턴스화합니다. 여기에 다중 샘플 안티앨리어싱 샘플 4개와 기본 지오메트리를 원하는 것을 지정하는 QSurfaceFormat 을 지정합니다. 애니메이션을 원하기 때문에 위에서 언급한 setAnimating() 함수를 참이라는 인수로 호출합니다.
int main(int argc, char **argv) { QGuiApplication app(argc, argv); QSurfaceFormat format; format.setSamples(16); TriangleWindow window; window.setFormat(format); window.resize(640, 480); window.show(); window.setAnimating(true); return app.exec(); }
다음 코드 스니펫은 이 예제에서 사용된 OpenGL 셰이더 프로그램을 보여줍니다. 버텍스 및 프래그먼트 셰이더는 버텍스 변환과 보간된 버텍스 컬러링을 수행하는 비교적 간단한 셰이더입니다.
static const char *vertexShaderSource = "attribute highp vec4 posAttr;\n" "attribute lowp vec4 colAttr;\n" "varying lowp vec4 col;\n" "uniform highp mat4 matrix;\n" "void main() {\n" " col = colAttr;\n" " gl_Position = matrix * posAttr;\n" "}\n"; static const char *fragmentShaderSource = "varying lowp vec4 col;\n" "void main() {\n" " gl_FragColor = col;\n" "}\n";
다음은 셰이더를 로드하고 셰이더 프로그램을 초기화하는 코드입니다. 원시 OpenGL 대신 QOpenGLShaderProgram 을 사용하면 데스크톱 OpenGL에서 표준에 포함되지 않는 highp, mediump, lowp 한정자를 제거할 수 있는 편리함을 얻을 수 있습니다. 매 프레임마다 위치를 조회할 필요가 없도록 멤버 변수에 속성과 균일한 위치를 저장합니다.
void TriangleWindow::initialize() { static const GLfloat vertices_colors[] = { +0.0f, +0.707f, 1.0f, 0.0f, 0.0f, -0.5f, -0.500f, 0.0f, 1.0f, 0.0f, +0.5f, -0.500f, 0.0f, 0.0f, 1.0f }; m_vbo.create(); m_vbo.bind(); m_vbo.allocate(vertices_colors, sizeof(vertices_colors)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), nullptr); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), reinterpret_cast<void *>(2 * sizeof(GLfloat))); m_program = new QOpenGLShaderProgram(this); m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); m_program->bindAttributeLocation("posAttr", 0); m_program->bindAttributeLocation("colAttr", 1); m_program->link(); m_program->bind(); m_matrixUniform = m_program->uniformLocation("matrix"); Q_ASSERT(m_matrixUniform != -1); }
마지막으로 렌더링() 함수는 OpenGL을 사용하여 뷰포트를 설정하고 배경을 지우고 회전하는 삼각형을 렌더링하는 함수입니다.
void TriangleWindow::render() { const qreal retinaScale = devicePixelRatio(); glViewport(0, 0, width() * retinaScale, height() * retinaScale); glClear(GL_COLOR_BUFFER_BIT); m_program->bind(); QMatrix4x4 matrix; matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f); matrix.translate(0, 0, -2); matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0); m_program->setUniformValue(m_matrixUniform, matrix); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); m_program->release(); ++m_frame; }
© 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.