qt 6.5.1 original
15
examples/opengl/2dpainting/2dpainting.pro
Normal file
@ -0,0 +1,15 @@
|
||||
QT += widgets opengl openglwidgets
|
||||
|
||||
HEADERS = glwidget.h \
|
||||
helper.h \
|
||||
widget.h \
|
||||
window.h
|
||||
SOURCES = glwidget.cpp \
|
||||
helper.cpp \
|
||||
main.cpp \
|
||||
widget.cpp \
|
||||
window.cpp
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/2dpainting
|
||||
INSTALLS += target
|
42
examples/opengl/2dpainting/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(2dpainting LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/2dpainting")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(2dpainting
|
||||
glwidget.cpp glwidget.h
|
||||
helper.cpp helper.h
|
||||
main.cpp
|
||||
widget.cpp widget.h
|
||||
window.cpp window.h
|
||||
)
|
||||
|
||||
set_target_properties(2dpainting PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(2dpainting PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS 2dpainting
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
37
examples/opengl/2dpainting/glwidget.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include "helper.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
//! [0]
|
||||
GLWidget::GLWidget(Helper *helper, QWidget *parent)
|
||||
: QOpenGLWidget(parent), helper(helper)
|
||||
{
|
||||
elapsed = 0;
|
||||
setFixedSize(200, 200);
|
||||
setAutoFillBackground(false);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void GLWidget::animate()
|
||||
{
|
||||
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
|
||||
update();
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
void GLWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter;
|
||||
painter.begin(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
helper->paint(&painter, event, elapsed);
|
||||
painter.end();
|
||||
}
|
||||
//! [2]
|
31
examples/opengl/2dpainting/glwidget.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
//! [0]
|
||||
class Helper;
|
||||
|
||||
class GLWidget : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GLWidget(Helper *helper, QWidget *parent);
|
||||
|
||||
public slots:
|
||||
void animate();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
Helper *helper;
|
||||
int elapsed;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
57
examples/opengl/2dpainting/helper.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
#include <QWidget>
|
||||
|
||||
//! [0]
|
||||
Helper::Helper()
|
||||
{
|
||||
QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
|
||||
gradient.setColorAt(0.0, Qt::white);
|
||||
gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
|
||||
|
||||
background = QBrush(QColor(64, 32, 64));
|
||||
circleBrush = QBrush(gradient);
|
||||
circlePen = QPen(Qt::black);
|
||||
circlePen.setWidth(1);
|
||||
textPen = QPen(Qt::white);
|
||||
textFont.setPixelSize(50);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
|
||||
{
|
||||
painter->fillRect(event->rect(), background);
|
||||
painter->translate(100, 100);
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
painter->save();
|
||||
painter->setBrush(circleBrush);
|
||||
painter->setPen(circlePen);
|
||||
painter->rotate(elapsed * 0.030);
|
||||
|
||||
qreal r = elapsed / 1000.0;
|
||||
int n = 30;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
painter->rotate(30);
|
||||
qreal factor = (i + r) / n;
|
||||
qreal radius = 0 + 120.0 * factor;
|
||||
qreal circleRadius = 1 + factor * 20;
|
||||
painter->drawEllipse(QRectF(radius, -circleRadius,
|
||||
circleRadius * 2, circleRadius * 2));
|
||||
}
|
||||
painter->restore();
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
painter->setPen(textPen);
|
||||
painter->setFont(textFont);
|
||||
painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, QStringLiteral("Qt"));
|
||||
}
|
||||
//! [3]
|
30
examples/opengl/2dpainting/helper.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef HELPER_H
|
||||
#define HELPER_H
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QPen>
|
||||
#include <QWidget>
|
||||
|
||||
//! [0]
|
||||
class Helper
|
||||
{
|
||||
public:
|
||||
Helper();
|
||||
|
||||
public:
|
||||
void paint(QPainter *painter, QPaintEvent *event, int elapsed);
|
||||
|
||||
private:
|
||||
QBrush background;
|
||||
QBrush circleBrush;
|
||||
QFont textFont;
|
||||
QPen circlePen;
|
||||
QPen textPen;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
20
examples/opengl/2dpainting/main.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setSamples(4);
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
Window window;
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
36
examples/opengl/2dpainting/widget.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "widget.h"
|
||||
#include "helper.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
//! [0]
|
||||
Widget::Widget(Helper *helper, QWidget *parent)
|
||||
: QWidget(parent), helper(helper)
|
||||
{
|
||||
elapsed = 0;
|
||||
setFixedSize(200, 200);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void Widget::animate()
|
||||
{
|
||||
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
|
||||
update();
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
void Widget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter;
|
||||
painter.begin(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
helper->paint(&painter, event, elapsed);
|
||||
painter.end();
|
||||
}
|
||||
//! [2]
|
31
examples/opengl/2dpainting/widget.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
//! [0]
|
||||
class Helper;
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(Helper *helper, QWidget *parent);
|
||||
|
||||
public slots:
|
||||
void animate();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
Helper *helper;
|
||||
int elapsed;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
36
examples/opengl/2dpainting/window.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include "widget.h"
|
||||
#include "window.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
//! [0]
|
||||
Window::Window()
|
||||
{
|
||||
setWindowTitle(tr("2D Painting on Native and OpenGL Widgets"));
|
||||
|
||||
Widget *native = new Widget(&helper, this);
|
||||
GLWidget *openGL = new GLWidget(&helper, this);
|
||||
QLabel *nativeLabel = new QLabel(tr("Native"));
|
||||
nativeLabel->setAlignment(Qt::AlignHCenter);
|
||||
QLabel *openGLLabel = new QLabel(tr("OpenGL"));
|
||||
openGLLabel->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->addWidget(native, 0, 0);
|
||||
layout->addWidget(openGL, 0, 1);
|
||||
layout->addWidget(nativeLabel, 1, 0);
|
||||
layout->addWidget(openGLLabel, 1, 1);
|
||||
setLayout(layout);
|
||||
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, native, &Widget::animate);
|
||||
connect(timer, &QTimer::timeout, openGL, &GLWidget::animate);
|
||||
timer->start(50);
|
||||
}
|
||||
//! [0]
|
24
examples/opengl/2dpainting/window.h
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
//! [0]
|
||||
class Window : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Window();
|
||||
|
||||
private:
|
||||
Helper helper;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
14
examples/opengl/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_example(openglwindow)
|
||||
qt_internal_add_example(hellogles3)
|
||||
if(TARGET Qt6::Widgets)
|
||||
qt_internal_add_example(contextinfo)
|
||||
qt_internal_add_example(2dpainting)
|
||||
qt_internal_add_example(hellogl2)
|
||||
qt_internal_add_example(qopenglwidget)
|
||||
qt_internal_add_example(cube)
|
||||
qt_internal_add_example(textures)
|
||||
qt_internal_add_example(stereoqopenglwidget)
|
||||
endif()
|
10
examples/opengl/README
Normal file
@ -0,0 +1,10 @@
|
||||
Qt provides support for integration with OpenGL implementations on all
|
||||
platforms, giving developers the opportunity to display hardware accelerated
|
||||
3D graphics alongside a more conventional user interface.
|
||||
|
||||
These examples demonstrate the basic techniques used to take advantage of
|
||||
OpenGL in Qt applications.
|
||||
|
||||
|
||||
Documentation for these examples can be found via the Examples
|
||||
link in the main Qt documentation.
|
39
examples/opengl/contextinfo/CMakeLists.txt
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(contextinfo LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/contextinfo")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(contextinfo
|
||||
main.cpp
|
||||
renderwindow.cpp renderwindow.h
|
||||
widget.cpp widget.h
|
||||
)
|
||||
|
||||
set_target_properties(contextinfo PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(contextinfo PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS contextinfo
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
14
examples/opengl/contextinfo/contextinfo.pro
Normal file
@ -0,0 +1,14 @@
|
||||
TEMPLATE = app
|
||||
QT += widgets opengl
|
||||
requires(qtConfig(filedialog))
|
||||
|
||||
SOURCES += main.cpp \
|
||||
widget.cpp \
|
||||
renderwindow.cpp
|
||||
|
||||
HEADERS += widget.h \
|
||||
renderwindow.h
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/contextinfo
|
||||
INSTALLS += target
|
25
examples/opengl/contextinfo/main.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include "widget.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!qstrcmp(argv[i], "-g"))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
|
||||
else if (!qstrcmp(argv[i], "-s"))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||
else if (!qstrcmp(argv[i], "-d"))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
||||
}
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
Widget w;
|
||||
w.resize(700, 800);
|
||||
w.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
191
examples/opengl/contextinfo/renderwindow.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "renderwindow.h"
|
||||
#include <QTimer>
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLContext>
|
||||
#include <QtOpenGL/QOpenGLShaderProgram>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
RenderWindow::RenderWindow(const QSurfaceFormat &format)
|
||||
: m_context(nullptr),
|
||||
m_initialized(false),
|
||||
m_forceGLSL110(false),
|
||||
m_angle(0.0f)
|
||||
{
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
setFormat(format);
|
||||
m_context = new QOpenGLContext(this);
|
||||
m_context->setFormat(requestedFormat());
|
||||
if (!m_context->create()) {
|
||||
delete m_context;
|
||||
m_context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderWindow::exposeEvent(QExposeEvent *)
|
||||
{
|
||||
if (isExposed())
|
||||
render();
|
||||
}
|
||||
|
||||
// ES needs the precision qualifiers.
|
||||
// On desktop GL QOpenGLShaderProgram inserts dummy defines for highp/mediump/lowp.
|
||||
static const char *vertexShaderSource110 =
|
||||
"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 *fragmentShaderSource110 =
|
||||
"varying lowp vec4 col;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = col;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *vertexShaderSource =
|
||||
"#version 150\n"
|
||||
"in vec4 posAttr;\n"
|
||||
"in vec4 colAttr;\n"
|
||||
"out vec4 col;\n"
|
||||
"uniform mat4 matrix;\n"
|
||||
"void main() {\n"
|
||||
" col = colAttr;\n"
|
||||
" gl_Position = matrix * posAttr;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *fragmentShaderSource =
|
||||
"#version 150\n"
|
||||
"in vec4 col;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"void main() {\n"
|
||||
" fragColor = col;\n"
|
||||
"}\n";
|
||||
|
||||
static GLfloat vertices[] = {
|
||||
0.0f, 0.707f,
|
||||
-0.5f, -0.5f,
|
||||
0.5f, -0.5f
|
||||
};
|
||||
|
||||
static GLfloat colors[] = {
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
void RenderWindow::init()
|
||||
{
|
||||
m_program = new QOpenGLShaderProgram(this);
|
||||
|
||||
QSurfaceFormat format = m_context->format();
|
||||
bool useNewStyleShader = format.profile() == QSurfaceFormat::CoreProfile;
|
||||
// Try to handle 3.0 & 3.1 that do not have the core/compatibility profile concept 3.2+ has.
|
||||
// This may still fail since version 150 (3.2) is specified in the sources but it's worth a try.
|
||||
if (format.renderableType() == QSurfaceFormat::OpenGL && format.majorVersion() == 3 && format.minorVersion() <= 1)
|
||||
useNewStyleShader = !format.testOption(QSurfaceFormat::DeprecatedFunctions);
|
||||
if (m_forceGLSL110)
|
||||
useNewStyleShader = false;
|
||||
|
||||
const char *vsrc = useNewStyleShader ? vertexShaderSource : vertexShaderSource110;
|
||||
const char *fsrc = useNewStyleShader ? fragmentShaderSource : fragmentShaderSource110;
|
||||
qDebug("Using version %s shader", useNewStyleShader ? "150" : "110");
|
||||
|
||||
if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc)) {
|
||||
emit error(m_program->log());
|
||||
return;
|
||||
}
|
||||
if (!m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc)) {
|
||||
emit error(m_program->log());
|
||||
return;
|
||||
}
|
||||
if (!m_program->link()) {
|
||||
emit error(m_program->log());
|
||||
return;
|
||||
}
|
||||
|
||||
m_posAttr = m_program->attributeLocation("posAttr");
|
||||
m_colAttr = m_program->attributeLocation("colAttr");
|
||||
m_matrixUniform = m_program->uniformLocation("matrix");
|
||||
|
||||
m_vbo.create();
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(vertices, sizeof(vertices) + sizeof(colors));
|
||||
m_vbo.write(sizeof(vertices), colors, sizeof(colors));
|
||||
m_vbo.release();
|
||||
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
||||
if (m_vao.isCreated()) // have VAO support, use it
|
||||
setupVertexAttribs();
|
||||
}
|
||||
|
||||
void RenderWindow::setupVertexAttribs()
|
||||
{
|
||||
m_vbo.bind();
|
||||
m_program->setAttributeBuffer(m_posAttr, GL_FLOAT, 0, 2);
|
||||
m_program->setAttributeBuffer(m_colAttr, GL_FLOAT, sizeof(vertices), 3);
|
||||
m_program->enableAttributeArray(m_posAttr);
|
||||
m_program->enableAttributeArray(m_colAttr);
|
||||
m_vbo.release();
|
||||
}
|
||||
|
||||
bool RenderWindow::event(QEvent *ev)
|
||||
{
|
||||
if (ev->type() == QEvent::UpdateRequest)
|
||||
render();
|
||||
return QWindow::event(ev);
|
||||
}
|
||||
|
||||
void RenderWindow::render()
|
||||
{
|
||||
if (!m_context->makeCurrent(this)) {
|
||||
emit error(tr("makeCurrent() failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
QOpenGLFunctions *f = m_context->functions();
|
||||
if (!m_initialized) {
|
||||
m_initialized = true;
|
||||
f->glEnable(GL_DEPTH_TEST);
|
||||
f->glClearColor(0, 0, 0, 1);
|
||||
init();
|
||||
emit ready();
|
||||
}
|
||||
|
||||
if (!m_vbo.isCreated()) // init() failed, don't bother with trying to render
|
||||
return;
|
||||
|
||||
const qreal retinaScale = devicePixelRatio();
|
||||
f->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
||||
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
m_program->bind();
|
||||
QMatrix4x4 matrix;
|
||||
matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
|
||||
matrix.translate(0.0f, 0.0f, -2.0f);
|
||||
matrix.rotate(m_angle, 0.0f, 1.0f, 0.0f);
|
||||
m_program->setUniformValue(m_matrixUniform, matrix);
|
||||
|
||||
if (m_vao.isCreated())
|
||||
m_vao.bind();
|
||||
else // no VAO support, set the vertex attribute arrays now
|
||||
setupVertexAttribs();
|
||||
|
||||
f->glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_vao.release();
|
||||
m_program->release();
|
||||
|
||||
// swapInterval is 1 by default which means that swapBuffers() will (hopefully) block
|
||||
// and wait for vsync.
|
||||
m_context->swapBuffers(this);
|
||||
|
||||
m_angle += 1.0f;
|
||||
|
||||
requestUpdate();
|
||||
}
|
48
examples/opengl/contextinfo/renderwindow.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef RENDERWINDOW_H
|
||||
#define RENDERWINDOW_H
|
||||
|
||||
#include <QWindow>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLContext)
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||
|
||||
class RenderWindow : public QWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RenderWindow(const QSurfaceFormat &format);
|
||||
QOpenGLContext *context() { return m_context; }
|
||||
void exposeEvent(QExposeEvent *) override;
|
||||
void setForceGLSL110(bool enable) { m_forceGLSL110 = enable; }
|
||||
|
||||
signals:
|
||||
void ready();
|
||||
void error(const QString &msg);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *ev) override;
|
||||
|
||||
private slots:
|
||||
void render();
|
||||
|
||||
private:
|
||||
void init();
|
||||
void setupVertexAttribs();
|
||||
|
||||
QOpenGLContext *m_context;
|
||||
bool m_initialized;
|
||||
bool m_forceGLSL110;
|
||||
QOpenGLShaderProgram *m_program;
|
||||
int m_posAttr, m_colAttr, m_matrixUniform;
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_vbo;
|
||||
float m_angle;
|
||||
};
|
||||
|
||||
#endif // RENDERWINDOW_H
|
360
examples/opengl/contextinfo/widget.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "widget.h"
|
||||
#include "renderwindow.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QRadioButton>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QList>
|
||||
#include <QByteArray>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QSplitter>
|
||||
#include <QGuiApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
|
||||
struct Version {
|
||||
const char *str;
|
||||
int major;
|
||||
int minor;
|
||||
};
|
||||
|
||||
static struct Version versions[] = {
|
||||
{ "1.0", 1, 0 },
|
||||
{ "1.1", 1, 1 },
|
||||
{ "1.2", 1, 2 },
|
||||
{ "1.3", 1, 3 },
|
||||
{ "1.4", 1, 4 },
|
||||
{ "1.5", 1, 5 },
|
||||
{ "2.0", 2, 0 },
|
||||
{ "2.1", 2, 1 },
|
||||
{ "3.0", 3, 0 },
|
||||
{ "3.1", 3, 1 },
|
||||
{ "3.2", 3, 2 },
|
||||
{ "3.3", 3, 3 },
|
||||
{ "4.0", 4, 0 },
|
||||
{ "4.1", 4, 1 },
|
||||
{ "4.2", 4, 2 },
|
||||
{ "4.3", 4, 3 },
|
||||
{ "4.4", 4, 4 },
|
||||
{ "4.5", 4, 5 }
|
||||
};
|
||||
|
||||
struct Profile {
|
||||
const char *str;
|
||||
QSurfaceFormat::OpenGLContextProfile profile;
|
||||
};
|
||||
|
||||
static struct Profile profiles[] = {
|
||||
{ "none", QSurfaceFormat::NoProfile },
|
||||
{ "core", QSurfaceFormat::CoreProfile },
|
||||
{ "compatibility", QSurfaceFormat::CompatibilityProfile }
|
||||
};
|
||||
|
||||
struct Option {
|
||||
const char *str;
|
||||
QSurfaceFormat::FormatOption option;
|
||||
};
|
||||
|
||||
static struct Option options[] = {
|
||||
{ "deprecated functions (not forward compatible)", QSurfaceFormat::DeprecatedFunctions },
|
||||
{ "debug context", QSurfaceFormat::DebugContext },
|
||||
{ "stereo buffers", QSurfaceFormat::StereoBuffers },
|
||||
// This is not a QSurfaceFormat option but is helpful to determine if the driver
|
||||
// allows compiling old-style shaders with core profile.
|
||||
{ "force version 110 shaders", QSurfaceFormat::FormatOption(0) }
|
||||
};
|
||||
|
||||
struct Renderable {
|
||||
const char *str;
|
||||
QSurfaceFormat::RenderableType renderable;
|
||||
};
|
||||
|
||||
static struct Renderable renderables[] = {
|
||||
{ "default", QSurfaceFormat::DefaultRenderableType },
|
||||
#ifndef Q_OS_ANDROID
|
||||
{ "OpenGL", QSurfaceFormat::OpenGL },
|
||||
#endif
|
||||
{ "OpenGL ES", QSurfaceFormat::OpenGLES }
|
||||
};
|
||||
|
||||
void Widget::addVersions(QLayout *layout)
|
||||
{
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
hbox->setSpacing(20);
|
||||
QLabel *label = new QLabel(tr("Context &version: "));
|
||||
hbox->addWidget(label);
|
||||
m_version = new QComboBox;
|
||||
m_version->setMinimumWidth(60);
|
||||
label->setBuddy(m_version);
|
||||
hbox->addWidget(m_version);
|
||||
for (size_t i = 0; i < sizeof(versions) / sizeof(Version); ++i) {
|
||||
m_version->addItem(QString::fromLatin1(versions[i].str));
|
||||
if (versions[i].major == 2 && versions[i].minor == 0)
|
||||
m_version->setCurrentIndex(m_version->count() - 1);
|
||||
}
|
||||
|
||||
QPushButton *btn = new QPushButton(tr("Create context"));
|
||||
connect(btn, &QPushButton::clicked, this, &Widget::start);
|
||||
btn->setMinimumSize(120, 40);
|
||||
hbox->addWidget(btn);
|
||||
|
||||
layout->addItem(hbox);
|
||||
}
|
||||
|
||||
void Widget::addProfiles(QLayout *layout)
|
||||
{
|
||||
QGroupBox *groupBox = new QGroupBox(tr("Profile"));
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
||||
vbox->addWidget(new QRadioButton(QString::fromLatin1(profiles[i].str)));
|
||||
static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
|
||||
groupBox->setLayout(vbox);
|
||||
layout->addWidget(groupBox);
|
||||
m_profiles = vbox;
|
||||
}
|
||||
|
||||
void Widget::addOptions(QLayout *layout)
|
||||
{
|
||||
QGroupBox *groupBox = new QGroupBox(tr("Options"));
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
||||
vbox->addWidget(new QCheckBox(QString::fromLatin1(options[i].str)));
|
||||
groupBox->setLayout(vbox);
|
||||
layout->addWidget(groupBox);
|
||||
m_options = vbox;
|
||||
}
|
||||
|
||||
void Widget::addRenderableTypes(QLayout *layout)
|
||||
{
|
||||
QGroupBox *groupBox = new QGroupBox(tr("Renderable type"));
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
||||
vbox->addWidget(new QRadioButton(QString::fromLatin1(renderables[i].str)));
|
||||
static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
|
||||
groupBox->setLayout(vbox);
|
||||
layout->addWidget(groupBox);
|
||||
m_renderables = vbox;
|
||||
}
|
||||
|
||||
void Widget::addRenderWindow()
|
||||
{
|
||||
m_renderWindowLayout->addWidget(m_renderWindowContainer);
|
||||
}
|
||||
|
||||
static QWidget *widgetWithLayout(QLayout *layout)
|
||||
{
|
||||
QWidget *w = new QWidget;
|
||||
w->setLayout(layout);
|
||||
return w;
|
||||
}
|
||||
|
||||
Widget::Widget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
QSplitter *vsplit = new QSplitter(Qt::Vertical);
|
||||
layout->addWidget(vsplit);
|
||||
|
||||
QSplitter *hsplit = new QSplitter;
|
||||
|
||||
QVBoxLayout *settingsLayout = new QVBoxLayout;
|
||||
addVersions(settingsLayout);
|
||||
addProfiles(settingsLayout);
|
||||
addOptions(settingsLayout);
|
||||
addRenderableTypes(settingsLayout);
|
||||
hsplit->addWidget(widgetWithLayout(settingsLayout));
|
||||
|
||||
QVBoxLayout *outputLayout = new QVBoxLayout;
|
||||
m_output = new QTextEdit;
|
||||
m_output->setReadOnly(true);
|
||||
outputLayout->addWidget(m_output);
|
||||
m_extensions = new QTextEdit;
|
||||
m_extensions->setReadOnly(true);
|
||||
outputLayout->addWidget(m_extensions);
|
||||
hsplit->addWidget(widgetWithLayout(outputLayout));
|
||||
|
||||
hsplit->setStretchFactor(0, 4);
|
||||
hsplit->setStretchFactor(1, 6);
|
||||
vsplit->addWidget(hsplit);
|
||||
|
||||
m_renderWindowLayout = new QVBoxLayout;
|
||||
vsplit->addWidget(widgetWithLayout(m_renderWindowLayout));
|
||||
vsplit->setStretchFactor(1, 5);
|
||||
|
||||
m_renderWindowContainer = new QWidget;
|
||||
addRenderWindow();
|
||||
|
||||
QString description;
|
||||
QTextStream str(&description);
|
||||
str << "Qt " << QT_VERSION_STR << ' ' << QGuiApplication::platformName();
|
||||
const char *openGlVariables[] =
|
||||
{"QT_ANGLE_PLATFORM", "QT_OPENGL", "QT_OPENGL_BUGLIST", "QT_OPENGL_DLL"};
|
||||
const size_t variableCount = sizeof(openGlVariables) / sizeof(openGlVariables[0]);
|
||||
for (size_t v = 0; v < variableCount; ++v) {
|
||||
if (qEnvironmentVariableIsSet(openGlVariables[v]))
|
||||
str << ' ' << openGlVariables[v] << '=' << qgetenv(openGlVariables[v]);
|
||||
}
|
||||
if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES))
|
||||
str << " Qt::AA_UseOpenGLES";
|
||||
if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
|
||||
str << " Qt::AA_UseSoftwareOpenGL";
|
||||
if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL))
|
||||
str << " Qt::AA_UseDesktopOpenGL";
|
||||
layout->addWidget(new QLabel(description));
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void Widget::start()
|
||||
{
|
||||
QSurfaceFormat fmt;
|
||||
|
||||
int idx = m_version->currentIndex();
|
||||
if (idx < 0)
|
||||
return;
|
||||
fmt.setVersion(versions[idx].major, versions[idx].minor);
|
||||
|
||||
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
||||
if (static_cast<QRadioButton *>(m_profiles->itemAt(int(i))->widget())->isChecked()) {
|
||||
fmt.setProfile(profiles[i].profile);
|
||||
break;
|
||||
}
|
||||
|
||||
bool forceGLSL110 = false;
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
||||
if (static_cast<QCheckBox *>(m_options->itemAt(int(i))->widget())->isChecked()) {
|
||||
if (options[i].option)
|
||||
fmt.setOption(options[i].option);
|
||||
else if (i == 3)
|
||||
forceGLSL110 = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
||||
if (static_cast<QRadioButton *>(m_renderables->itemAt(int(i))->widget())->isChecked()) {
|
||||
fmt.setRenderableType(renderables[i].renderable);
|
||||
break;
|
||||
}
|
||||
|
||||
// The example rendering will need a depth buffer.
|
||||
fmt.setDepthBufferSize(16);
|
||||
|
||||
m_output->clear();
|
||||
m_extensions->clear();
|
||||
qDebug() << "Requesting surface format" << fmt;
|
||||
|
||||
m_renderWindowLayout->removeWidget(m_renderWindowContainer);
|
||||
delete m_renderWindowContainer;
|
||||
|
||||
RenderWindow *renderWindow = new RenderWindow(fmt);
|
||||
if (!renderWindow->context()) {
|
||||
m_output->append(tr("Failed to create context"));
|
||||
delete renderWindow;
|
||||
m_renderWindowContainer = new QWidget;
|
||||
addRenderWindow();
|
||||
return;
|
||||
}
|
||||
m_surface = renderWindow;
|
||||
|
||||
renderWindow->setForceGLSL110(forceGLSL110);
|
||||
connect(renderWindow, &RenderWindow::ready, this, &Widget::renderWindowReady);
|
||||
connect(renderWindow, &RenderWindow::error, this, &Widget::renderWindowError);
|
||||
|
||||
m_renderWindowContainer = QWidget::createWindowContainer(renderWindow);
|
||||
addRenderWindow();
|
||||
}
|
||||
|
||||
void Widget::printFormat(const QSurfaceFormat &format)
|
||||
{
|
||||
m_output->append(tr("OpenGL version: %1.%2").arg(format.majorVersion()).arg(format.minorVersion()));
|
||||
|
||||
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
||||
if (profiles[i].profile == format.profile()) {
|
||||
m_output->append(tr("Profile: %1").arg(QString::fromLatin1(profiles[i].str)));
|
||||
break;
|
||||
}
|
||||
|
||||
QString opts;
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
||||
if (format.testOption(options[i].option))
|
||||
opts += QString::fromLatin1(options[i].str) + QLatin1Char(' ');
|
||||
m_output->append(tr("Options: %1").arg(opts));
|
||||
|
||||
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
||||
if (renderables[i].renderable == format.renderableType()) {
|
||||
m_output->append(tr("Renderable type: %1").arg(QString::fromLatin1(renderables[i].str)));
|
||||
break;
|
||||
}
|
||||
|
||||
m_output->append(tr("Depth buffer size: %1").arg(QString::number(format.depthBufferSize())));
|
||||
m_output->append(tr("Stencil buffer size: %1").arg(QString::number(format.stencilBufferSize())));
|
||||
m_output->append(tr("Samples: %1").arg(QString::number(format.samples())));
|
||||
m_output->append(tr("Red buffer size: %1").arg(QString::number(format.redBufferSize())));
|
||||
m_output->append(tr("Green buffer size: %1").arg(QString::number(format.greenBufferSize())));
|
||||
m_output->append(tr("Blue buffer size: %1").arg(QString::number(format.blueBufferSize())));
|
||||
m_output->append(tr("Alpha buffer size: %1").arg(QString::number(format.alphaBufferSize())));
|
||||
m_output->append(tr("Swap interval: %1").arg(QString::number(format.swapInterval())));
|
||||
}
|
||||
|
||||
void Widget::renderWindowReady()
|
||||
{
|
||||
QOpenGLContext *context = QOpenGLContext::currentContext();
|
||||
Q_ASSERT(context);
|
||||
|
||||
QString vendor, renderer, version, glslVersion;
|
||||
const GLubyte *p;
|
||||
QOpenGLFunctions *f = context->functions();
|
||||
if ((p = f->glGetString(GL_VENDOR)))
|
||||
vendor = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
||||
if ((p = f->glGetString(GL_RENDERER)))
|
||||
renderer = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
||||
if ((p = f->glGetString(GL_VERSION)))
|
||||
version = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
||||
if ((p = f->glGetString(GL_SHADING_LANGUAGE_VERSION)))
|
||||
glslVersion = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
||||
|
||||
m_output->append(tr("*** Context information ***"));
|
||||
m_output->append(tr("Vendor: %1").arg(vendor));
|
||||
m_output->append(tr("Renderer: %1").arg(renderer));
|
||||
m_output->append(tr("OpenGL version: %1").arg(version));
|
||||
m_output->append(tr("GLSL version: %1").arg(glslVersion));
|
||||
|
||||
m_output->append(tr("\n*** QSurfaceFormat from context ***"));
|
||||
printFormat(context->format());
|
||||
|
||||
m_output->append(tr("\n*** QSurfaceFormat from window surface ***"));
|
||||
printFormat(m_surface->format());
|
||||
|
||||
m_output->append(tr("\n*** Qt build information ***"));
|
||||
const char *gltype[] = { "Desktop", "GLES 2", "GLES 1" };
|
||||
m_output->append(tr("Qt OpenGL configuration: %1")
|
||||
.arg(QString::fromLatin1(gltype[QOpenGLContext::openGLModuleType()])));
|
||||
#if defined(Q_OS_WIN)
|
||||
using namespace QNativeInterface;
|
||||
m_output->append(tr("Qt OpenGL library handle: %1")
|
||||
.arg(QString::number(qintptr(QWGLContext::openGLModuleHandle()), 16)));
|
||||
#endif
|
||||
|
||||
QList<QByteArray> extensionList = context->extensions().values();
|
||||
std::sort(extensionList.begin(), extensionList.end());
|
||||
m_extensions->append(tr("Found %1 extensions:").arg(extensionList.count()));
|
||||
for (const QByteArray &ext : std::as_const(extensionList))
|
||||
m_extensions->append(QString::fromLatin1(ext));
|
||||
|
||||
m_output->moveCursor(QTextCursor::Start);
|
||||
m_extensions->moveCursor(QTextCursor::Start);
|
||||
}
|
||||
|
||||
void Widget::renderWindowError(const QString &msg)
|
||||
{
|
||||
m_output->append(tr("An error has occurred:\n%1").arg(msg));
|
||||
}
|
46
examples/opengl/contextinfo/widget.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QComboBox)
|
||||
QT_FORWARD_DECLARE_CLASS(QTextEdit)
|
||||
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QSurfaceFormat)
|
||||
QT_FORWARD_DECLARE_CLASS(QSurface)
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Widget(QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void start();
|
||||
void renderWindowReady();
|
||||
void renderWindowError(const QString &msg);
|
||||
|
||||
private:
|
||||
void addVersions(QLayout *layout);
|
||||
void addProfiles(QLayout *layout);
|
||||
void addOptions(QLayout *layout);
|
||||
void addRenderableTypes(QLayout *layout);
|
||||
void addRenderWindow();
|
||||
void printFormat(const QSurfaceFormat &format);
|
||||
|
||||
QComboBox *m_version;
|
||||
QLayout *m_profiles;
|
||||
QLayout *m_options;
|
||||
QLayout *m_renderables;
|
||||
QTextEdit *m_output;
|
||||
QTextEdit *m_extensions;
|
||||
QVBoxLayout *m_renderWindowLayout;
|
||||
QWidget *m_renderWindowContainer;
|
||||
QSurface *m_surface;
|
||||
};
|
||||
|
||||
#endif // WIDGET_H
|
64
examples/opengl/cube/CMakeLists.txt
Normal file
@ -0,0 +1,64 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(cube LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/cube")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(cube
|
||||
geometryengine.cpp geometryengine.h
|
||||
main.cpp
|
||||
mainwidget.cpp mainwidget.h
|
||||
)
|
||||
|
||||
set_target_properties(cube PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(cube PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(shaders_resource_files
|
||||
"fshader.glsl"
|
||||
"vshader.glsl"
|
||||
)
|
||||
|
||||
qt6_add_resources(cube "shaders"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${shaders_resource_files}
|
||||
)
|
||||
|
||||
set(textures_resource_files
|
||||
"cube.png"
|
||||
)
|
||||
|
||||
qt6_add_resources(cube "textures"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${textures_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS cube
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
BIN
examples/opengl/cube/cube.png
Normal file
After Width: | Height: | Size: 87 KiB |
22
examples/opengl/cube/cube.pro
Normal file
@ -0,0 +1,22 @@
|
||||
QT += core gui widgets opengl openglwidgets
|
||||
|
||||
TARGET = cube
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += main.cpp
|
||||
|
||||
SOURCES += \
|
||||
mainwidget.cpp \
|
||||
geometryengine.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwidget.h \
|
||||
geometryengine.h
|
||||
|
||||
RESOURCES += \
|
||||
shaders.qrc \
|
||||
textures.qrc
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/cube
|
||||
INSTALLS += target
|
18
examples/opengl/cube/fshader.glsl
Normal file
@ -0,0 +1,18 @@
|
||||
#ifdef GL_ES
|
||||
// Set default precision to medium
|
||||
precision mediump int;
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
//! [0]
|
||||
void main()
|
||||
{
|
||||
// Set fragment color from texture
|
||||
gl_FragColor = texture2D(texture, v_texcoord);
|
||||
}
|
||||
//! [0]
|
||||
|
132
examples/opengl/cube/geometryengine.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "geometryengine.h"
|
||||
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
|
||||
struct VertexData
|
||||
{
|
||||
QVector3D position;
|
||||
QVector2D texCoord;
|
||||
};
|
||||
|
||||
//! [0]
|
||||
GeometryEngine::GeometryEngine()
|
||||
: indexBuf(QOpenGLBuffer::IndexBuffer)
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
// Generate 2 VBOs
|
||||
arrayBuf.create();
|
||||
indexBuf.create();
|
||||
|
||||
// Initializes cube geometry and transfers it to VBOs
|
||||
initCubeGeometry();
|
||||
}
|
||||
|
||||
GeometryEngine::~GeometryEngine()
|
||||
{
|
||||
arrayBuf.destroy();
|
||||
indexBuf.destroy();
|
||||
}
|
||||
//! [0]
|
||||
|
||||
void GeometryEngine::initCubeGeometry()
|
||||
{
|
||||
// For cube we would need only 8 vertices but we have to
|
||||
// duplicate vertex for each face because texture coordinate
|
||||
// is different.
|
||||
VertexData vertices[] = {
|
||||
// Vertex data for face 0
|
||||
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.0f, 0.0f)}, // v0
|
||||
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.0f)}, // v1
|
||||
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.5f)}, // v2
|
||||
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v3
|
||||
|
||||
// Vertex data for face 1
|
||||
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D( 0.0f, 0.5f)}, // v4
|
||||
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.5f)}, // v5
|
||||
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)}, // v6
|
||||
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v7
|
||||
|
||||
// Vertex data for face 2
|
||||
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v8
|
||||
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(1.0f, 0.5f)}, // v9
|
||||
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v10
|
||||
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(1.0f, 1.0f)}, // v11
|
||||
|
||||
// Vertex data for face 3
|
||||
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v12
|
||||
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(1.0f, 0.0f)}, // v13
|
||||
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v14
|
||||
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(1.0f, 0.5f)}, // v15
|
||||
|
||||
// Vertex data for face 4
|
||||
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.0f)}, // v16
|
||||
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v17
|
||||
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v18
|
||||
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v19
|
||||
|
||||
// Vertex data for face 5
|
||||
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v20
|
||||
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v21
|
||||
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v22
|
||||
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)} // v23
|
||||
};
|
||||
|
||||
// Indices for drawing cube faces using triangle strips.
|
||||
// Triangle strips can be connected by duplicating indices
|
||||
// between the strips. If connecting strips have opposite
|
||||
// vertex order then last index of the first strip and first
|
||||
// index of the second strip needs to be duplicated. If
|
||||
// connecting strips have same vertex order then only last
|
||||
// index of the first strip needs to be duplicated.
|
||||
GLushort indices[] = {
|
||||
0, 1, 2, 3, 3, // Face 0 - triangle strip ( v0, v1, v2, v3)
|
||||
4, 4, 5, 6, 7, 7, // Face 1 - triangle strip ( v4, v5, v6, v7)
|
||||
8, 8, 9, 10, 11, 11, // Face 2 - triangle strip ( v8, v9, v10, v11)
|
||||
12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15)
|
||||
16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19)
|
||||
20, 20, 21, 22, 23 // Face 5 - triangle strip (v20, v21, v22, v23)
|
||||
};
|
||||
|
||||
//! [1]
|
||||
// Transfer vertex data to VBO 0
|
||||
arrayBuf.bind();
|
||||
arrayBuf.allocate(vertices, 24 * sizeof(VertexData));
|
||||
|
||||
// Transfer index data to VBO 1
|
||||
indexBuf.bind();
|
||||
indexBuf.allocate(indices, 34 * sizeof(GLushort));
|
||||
//! [1]
|
||||
}
|
||||
|
||||
//! [2]
|
||||
void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program)
|
||||
{
|
||||
// Tell OpenGL which VBOs to use
|
||||
arrayBuf.bind();
|
||||
indexBuf.bind();
|
||||
|
||||
// Offset for position
|
||||
quintptr offset = 0;
|
||||
|
||||
// Tell OpenGL programmable pipeline how to locate vertex position data
|
||||
int vertexLocation = program->attributeLocation("a_position");
|
||||
program->enableAttributeArray(vertexLocation);
|
||||
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
|
||||
|
||||
// Offset for texture coordinate
|
||||
offset += sizeof(QVector3D);
|
||||
|
||||
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
|
||||
int texcoordLocation = program->attributeLocation("a_texcoord");
|
||||
program->enableAttributeArray(texcoordLocation);
|
||||
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
|
||||
|
||||
// Draw cube geometry using indices from VBO 1
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr);
|
||||
}
|
||||
//! [2]
|
26
examples/opengl/cube/geometryengine.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GEOMETRYENGINE_H
|
||||
#define GEOMETRYENGINE_H
|
||||
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLBuffer>
|
||||
|
||||
class GeometryEngine : protected QOpenGLFunctions
|
||||
{
|
||||
public:
|
||||
GeometryEngine();
|
||||
virtual ~GeometryEngine();
|
||||
|
||||
void drawCubeGeometry(QOpenGLShaderProgram *program);
|
||||
|
||||
private:
|
||||
void initCubeGeometry();
|
||||
|
||||
QOpenGLBuffer arrayBuf;
|
||||
QOpenGLBuffer indexBuf;
|
||||
};
|
||||
|
||||
#endif // GEOMETRYENGINE_H
|
30
examples/opengl/cube/main.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
#ifndef QT_NO_OPENGL
|
||||
#include "mainwidget.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
app.setApplicationName("cube");
|
||||
app.setApplicationVersion("0.1");
|
||||
#ifndef QT_NO_OPENGL
|
||||
MainWidget widget;
|
||||
widget.show();
|
||||
#else
|
||||
QLabel note("OpenGL Support required");
|
||||
note.show();
|
||||
#endif
|
||||
return app.exec();
|
||||
}
|
168
examples/opengl/cube/mainwidget.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwidget.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
MainWidget::~MainWidget()
|
||||
{
|
||||
// Make sure the context is current when deleting the texture
|
||||
// and the buffers.
|
||||
makeCurrent();
|
||||
delete texture;
|
||||
delete geometries;
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
//! [0]
|
||||
void MainWidget::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
// Save mouse press position
|
||||
mousePressPosition = QVector2D(e->position());
|
||||
}
|
||||
|
||||
void MainWidget::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
// Mouse release position - mouse press position
|
||||
QVector2D diff = QVector2D(e->position()) - mousePressPosition;
|
||||
|
||||
// Rotation axis is perpendicular to the mouse position difference
|
||||
// vector
|
||||
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
|
||||
|
||||
// Accelerate angular speed relative to the length of the mouse sweep
|
||||
qreal acc = diff.length() / 100.0;
|
||||
|
||||
// Calculate new rotation axis as weighted sum
|
||||
rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
|
||||
|
||||
// Increase angular speed
|
||||
angularSpeed += acc;
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void MainWidget::timerEvent(QTimerEvent *)
|
||||
{
|
||||
// Decrease angular speed (friction)
|
||||
angularSpeed *= 0.99;
|
||||
|
||||
// Stop rotation when speed goes below threshold
|
||||
if (angularSpeed < 0.01) {
|
||||
angularSpeed = 0.0;
|
||||
} else {
|
||||
// Update rotation
|
||||
rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
|
||||
|
||||
// Request an update
|
||||
update();
|
||||
}
|
||||
}
|
||||
//! [1]
|
||||
|
||||
void MainWidget::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
|
||||
initShaders();
|
||||
initTextures();
|
||||
|
||||
geometries = new GeometryEngine;
|
||||
|
||||
// Use QBasicTimer because its faster than QTimer
|
||||
timer.start(12, this);
|
||||
}
|
||||
|
||||
//! [3]
|
||||
void MainWidget::initShaders()
|
||||
{
|
||||
// Compile vertex shader
|
||||
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl"))
|
||||
close();
|
||||
|
||||
// Compile fragment shader
|
||||
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl"))
|
||||
close();
|
||||
|
||||
// Link shader pipeline
|
||||
if (!program.link())
|
||||
close();
|
||||
|
||||
// Bind shader pipeline for use
|
||||
if (!program.bind())
|
||||
close();
|
||||
}
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
void MainWidget::initTextures()
|
||||
{
|
||||
// Load cube.png image
|
||||
texture = new QOpenGLTexture(QImage(":/cube.png").mirrored());
|
||||
|
||||
// Set nearest filtering mode for texture minification
|
||||
texture->setMinificationFilter(QOpenGLTexture::Nearest);
|
||||
|
||||
// Set bilinear filtering mode for texture magnification
|
||||
texture->setMagnificationFilter(QOpenGLTexture::Linear);
|
||||
|
||||
// Wrap texture coordinates by repeating
|
||||
// f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
|
||||
texture->setWrapMode(QOpenGLTexture::Repeat);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
void MainWidget::resizeGL(int w, int h)
|
||||
{
|
||||
// Calculate aspect ratio
|
||||
qreal aspect = qreal(w) / qreal(h ? h : 1);
|
||||
|
||||
// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
|
||||
const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
|
||||
|
||||
// Reset projection
|
||||
projection.setToIdentity();
|
||||
|
||||
// Set perspective projection
|
||||
projection.perspective(fov, aspect, zNear, zFar);
|
||||
}
|
||||
//! [5]
|
||||
|
||||
void MainWidget::paintGL()
|
||||
{
|
||||
// Clear color and depth buffer
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//! [2]
|
||||
// Enable depth buffer
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Enable back face culling
|
||||
glEnable(GL_CULL_FACE);
|
||||
//! [2]
|
||||
|
||||
texture->bind();
|
||||
program.bind();
|
||||
|
||||
//! [6]
|
||||
// Calculate model view transformation
|
||||
QMatrix4x4 matrix;
|
||||
matrix.translate(0.0, 0.0, -5.0);
|
||||
matrix.rotate(rotation);
|
||||
|
||||
// Set modelview-projection matrix
|
||||
program.setUniformValue("mvp_matrix", projection * matrix);
|
||||
//! [6]
|
||||
|
||||
// Use texture unit 0 which contains cube.png
|
||||
program.setUniformValue("texture", 0);
|
||||
|
||||
// Draw cube geometry
|
||||
geometries->drawCubeGeometry(&program);
|
||||
}
|
55
examples/opengl/cube/mainwidget.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef MAINWIDGET_H
|
||||
#define MAINWIDGET_H
|
||||
|
||||
#include "geometryengine.h"
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QMatrix4x4>
|
||||
#include <QQuaternion>
|
||||
#include <QVector2D>
|
||||
#include <QBasicTimer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
|
||||
class GeometryEngine;
|
||||
|
||||
class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QOpenGLWidget::QOpenGLWidget;
|
||||
~MainWidget();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
void initializeGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
void paintGL() override;
|
||||
|
||||
void initShaders();
|
||||
void initTextures();
|
||||
|
||||
private:
|
||||
QBasicTimer timer;
|
||||
QOpenGLShaderProgram program;
|
||||
GeometryEngine *geometries = nullptr;
|
||||
|
||||
QOpenGLTexture *texture = nullptr;
|
||||
|
||||
QMatrix4x4 projection;
|
||||
|
||||
QVector2D mousePressPosition;
|
||||
QVector3D rotationAxis;
|
||||
qreal angularSpeed = 0;
|
||||
QQuaternion rotation;
|
||||
};
|
||||
|
||||
#endif // MAINWIDGET_H
|
6
examples/opengl/cube/shaders.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>vshader.glsl</file>
|
||||
<file>fshader.glsl</file>
|
||||
</qresource>
|
||||
</RCC>
|
5
examples/opengl/cube/textures.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>cube.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
24
examples/opengl/cube/vshader.glsl
Normal file
@ -0,0 +1,24 @@
|
||||
#ifdef GL_ES
|
||||
// Set default precision to medium
|
||||
precision mediump int;
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform mat4 mvp_matrix;
|
||||
|
||||
attribute vec4 a_position;
|
||||
attribute vec2 a_texcoord;
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
//! [0]
|
||||
void main()
|
||||
{
|
||||
// Calculate vertex position in screen space
|
||||
gl_Position = mvp_matrix * a_position;
|
||||
|
||||
// Pass texture coordinate to fragment shader
|
||||
// Value will be automatically interpolated to fragments inside polygon faces
|
||||
v_texcoord = a_texcoord;
|
||||
}
|
||||
//! [0]
|
BIN
examples/opengl/doc/images/2dpainting-example.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
examples/opengl/doc/images/cube.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
examples/opengl/doc/images/cube_faces.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
examples/opengl/doc/images/hellogl2-example.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
examples/opengl/doc/images/hellogles3-example.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
examples/opengl/doc/images/stereoexample-leftbuffer.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
examples/opengl/doc/images/stereoexample-rightbuffer.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
examples/opengl/doc/images/textures-example.png
Normal file
After Width: | Height: | Size: 46 KiB |
184
examples/opengl/doc/src/2dpainting.qdoc
Normal file
@ -0,0 +1,184 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example 2dpainting
|
||||
\title 2D Painting Example
|
||||
\ingroup examples-widgets-opengl
|
||||
|
||||
\brief The 2D Painting example shows how QPainter and QOpenGLWidget can be
|
||||
used together to display accelerated 2D graphics on supported hardware.
|
||||
|
||||
\image 2dpainting-example.png
|
||||
|
||||
The QPainter class is used to draw 2D graphics primitives onto
|
||||
paint devices provided by QPaintDevice subclasses, such as QWidget
|
||||
and QImage.
|
||||
|
||||
Since QOpenGLWidget is a subclass of QWidget, it is possible
|
||||
to reimplement its \l{QWidget::paintEvent()}{paintEvent()} and use
|
||||
QPainter to draw on the device, just as you would with a QWidget.
|
||||
The only difference is that the painting operations will be accelerated
|
||||
in hardware if it is supported by your system's OpenGL drivers.
|
||||
|
||||
In this example, we perform the same painting operations on a
|
||||
QWidget and a QOpenGLWidget. The QWidget is shown with anti-aliasing
|
||||
enabled, and the QOpenGLWidget will also use anti-aliasing if the
|
||||
required extensions are supported by your system's OpenGL driver.
|
||||
|
||||
\section1 Overview
|
||||
|
||||
To be able to compare the results of painting onto a QOpenGLWidget subclass
|
||||
with native drawing in a QWidget subclass, we want to show both kinds
|
||||
of widget side by side. To do this, we derive subclasses of QWidget and
|
||||
QOpenGLWidget, using a separate \c Helper class to perform the same painting
|
||||
operations for each, and lay them out in a top-level widget, itself
|
||||
provided a the \c Window class.
|
||||
|
||||
\section1 Helper Class Definition
|
||||
|
||||
In this example, the painting operations are performed by a helper class.
|
||||
We do this because we want the same painting operations to be performed
|
||||
for both our QWidget subclass and the QOpenGLWidget subclass.
|
||||
|
||||
The \c Helper class is minimal:
|
||||
|
||||
\snippet 2dpainting/helper.h 0
|
||||
|
||||
Apart from the constructor, it only provides a \c paint() function to paint
|
||||
using a painter supplied by one of our widget subclasses.
|
||||
|
||||
\section1 Helper Class Implementation
|
||||
|
||||
The constructor of the class sets up the resources it needs to paint
|
||||
content onto a widget:
|
||||
|
||||
\snippet 2dpainting/helper.cpp 0
|
||||
|
||||
The actual painting is performed in the \c paint() function. This takes
|
||||
a QPainter that has already been set up to paint onto a paint device
|
||||
(either a QWidget or a QOpenGLWidget), a QPaintEvent that provides information
|
||||
about the region to be painted, and a measure of the elapsed time (in
|
||||
milliseconds) since the paint device was last updated.
|
||||
|
||||
\snippet 2dpainting/helper.cpp 1
|
||||
|
||||
We begin painting by filling in the region contained in the paint event
|
||||
before translating the origin of the coordinate system so that the rest
|
||||
of the painting operations will be displaced towards the center of the
|
||||
paint device.
|
||||
|
||||
We draw a spiral pattern of circles, using the elapsed time specified to
|
||||
animate them so that they appear to move outward and around the coordinate
|
||||
system's origin:
|
||||
|
||||
\snippet 2dpainting/helper.cpp 2
|
||||
|
||||
Since the coordinate system is rotated many times during
|
||||
this process, we \l{QPainter::save()}{save()} the QPainter's state
|
||||
beforehand and \l{QPainter::restore()}{restore()} it afterwards.
|
||||
|
||||
\snippet 2dpainting/helper.cpp 3
|
||||
|
||||
We draw some text at the origin to complete the effect.
|
||||
|
||||
\section1 Widget Class Definition
|
||||
|
||||
The \c Widget class provides a basic custom widget that we use to
|
||||
display the simple animation painted by the \c Helper class.
|
||||
|
||||
\snippet 2dpainting/widget.h 0
|
||||
|
||||
Apart from the constructor, it only contains a
|
||||
\l{QWidget::paintEvent()}{paintEvent()} function, that lets us draw
|
||||
customized content, and a slot that is used to animate its contents.
|
||||
One member variable keeps track of the \c Helper that the widget uses
|
||||
to paint its contents, and the other records the elapsed time since
|
||||
it was last updated.
|
||||
|
||||
\section1 Widget Class Implementation
|
||||
|
||||
The constructor only initializes the member variables, storing the
|
||||
\c Helper object supplied and calling the base class's constructor,
|
||||
and enforces a fixed size for the widget:
|
||||
|
||||
\snippet 2dpainting/widget.cpp 0
|
||||
|
||||
The \c animate() slot is called whenever a timer, which we define later, times
|
||||
out:
|
||||
|
||||
\snippet 2dpainting/widget.cpp 1
|
||||
|
||||
Here, we determine the interval that has elapsed since the timer last
|
||||
timed out, and we add it to any existing value before repainting the
|
||||
widget. Since the animation used in the \c Helper class loops every second,
|
||||
we can use the modulo operator to ensure that the \c elapsed variable is
|
||||
always less than 1000.
|
||||
|
||||
Since the \c Helper class does all of the actual painting, we only have
|
||||
to implement a paint event that sets up a QPainter for the widget and calls
|
||||
the helper's \c paint() function:
|
||||
|
||||
\snippet 2dpainting/widget.cpp 2
|
||||
|
||||
\section1 GLWidget Class Definition
|
||||
|
||||
The \c GLWidget class definition is basically the same as the \c Widget
|
||||
class except that it is derived from QOpenGLWidget.
|
||||
|
||||
\snippet 2dpainting/glwidget.h 0
|
||||
|
||||
Again, the member variables record the \c Helper used to paint the
|
||||
widget and the elapsed time since the previous update.
|
||||
|
||||
\section1 GLWidget Class Implementation
|
||||
|
||||
The constructor differs a little from the \c Widget class's constructor:
|
||||
|
||||
\snippet 2dpainting/glwidget.cpp 0
|
||||
|
||||
The \c elapsed member variable is initialized and the \c Helper object used
|
||||
to paint the widget is stored.
|
||||
|
||||
The \c animate() slot is exactly the same as that provided by the \c Widget
|
||||
class:
|
||||
|
||||
\snippet 2dpainting/glwidget.cpp 1
|
||||
|
||||
The \c paintEvent() is almost the same as that found in the \c Widget class:
|
||||
|
||||
\snippet 2dpainting/glwidget.cpp 2
|
||||
|
||||
Since anti-aliasing will be enabled if available, we only need to set up
|
||||
a QPainter on the widget and call the helper's \c paint() function to display
|
||||
the widget's contents.
|
||||
|
||||
\section1 Window Class Definition
|
||||
|
||||
The \c Window class has a basic, minimal definition:
|
||||
|
||||
\snippet 2dpainting/window.h 0
|
||||
|
||||
It contains a single \c Helper object that will be shared between all
|
||||
widgets.
|
||||
|
||||
\section1 Window Class Implementation
|
||||
|
||||
The constructor does all the work, creating a widget of each type and
|
||||
inserting them with labels into a layout:
|
||||
|
||||
\snippet 2dpainting/window.cpp 0
|
||||
|
||||
A timer with a 50 millisecond time out is constructed for animation purposes,
|
||||
and connected to the \c animate() slots of the \c Widget and \c GLWidget objects.
|
||||
Once started, the widgets should be updated at around 20 frames per second.
|
||||
|
||||
\section1 Running the Example
|
||||
|
||||
The example shows the same painting operations performed at the same time
|
||||
in a \c Widget and a \c GLWidget. The quality and speed of rendering in the
|
||||
\c GLWidget depends on the level of support for multisampling and hardware
|
||||
acceleration that your system's OpenGL driver provides. If support for either
|
||||
of these is lacking, the driver may fall back on a software renderer that
|
||||
may trade quality for speed.
|
||||
*/
|
142
examples/opengl/doc/src/cube.qdoc
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example cube
|
||||
\ingroup examples-widgets-opengl
|
||||
\title Cube OpenGL ES 2.0 example
|
||||
|
||||
\brief The Cube OpenGL ES 2.0 example shows how to write mouse rotatable
|
||||
textured 3D cube using OpenGL ES 2.0 with Qt. It shows how to handle
|
||||
polygon geometries efficiently and how to write simple vertex and
|
||||
fragment shader for programmable graphics pipeline. In addition it
|
||||
shows how to use quaternions for representing 3D object orientation.
|
||||
|
||||
This example has been written for OpenGL ES 2.0 but it works also on
|
||||
desktop OpenGL because this example is simple enough and for the
|
||||
most parts desktop OpenGL API is same. It compiles also without OpenGL
|
||||
support but then it just shows a label stating that OpenGL support is
|
||||
required.
|
||||
|
||||
\image cube.png Screenshot of the Cube example running on N900
|
||||
|
||||
The example consist of two classes:
|
||||
|
||||
\list
|
||||
\li \c MainWidget extends QOpenGLWidget and contains OpenGL ES 2.0
|
||||
initialization and drawing and mouse and timer event handling
|
||||
\li \c GeometryEngine handles polygon geometries. Transfers polygon geometry
|
||||
to vertex buffer objects and draws geometries from vertex buffer objects.
|
||||
\endlist
|
||||
|
||||
We'll start by initializing OpenGL ES 2.0 in \c MainWidget.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section1 Initializing OpenGL ES 2.0
|
||||
|
||||
Since OpenGL ES 2.0 doesn't support fixed graphics pipeline anymore it has to
|
||||
be implemented by ourselves. This makes graphics pipeline very flexible but
|
||||
in the same time it becomes more difficult because user has to implement graphics
|
||||
pipeline to get even the simplest example running. It also makes graphics pipeline
|
||||
more efficient because user can decide what kind of pipeline is needed for the
|
||||
application.
|
||||
|
||||
First we have to implement vertex shader. It gets vertex data and
|
||||
model-view-projection matrix (MVP) as parameters. It transforms vertex position
|
||||
using MVP matrix to screen space and passes texture coordinate to
|
||||
fragment shader. Texture coordinate will be automatically interpolated on polygon
|
||||
faces.
|
||||
|
||||
\snippet cube/vshader.glsl 0
|
||||
|
||||
After that we need to implement second part of the graphics pipeline - fragment
|
||||
shader. For this exercise we need to implement fragment shader that handles
|
||||
texturing. It gets interpolated texture coordinate as a parameter and looks up
|
||||
fragment color from the given texture.
|
||||
|
||||
\snippet cube/fshader.glsl 0
|
||||
|
||||
Using QOpenGLShaderProgram we can compile, link and bind shader code to
|
||||
graphics pipeline. This code uses Qt Resource files to access shader source code.
|
||||
|
||||
\snippet cube/mainwidget.cpp 3
|
||||
|
||||
The following code enables depth buffering and back face culling.
|
||||
|
||||
\snippet cube/mainwidget.cpp 2
|
||||
|
||||
\section1 Loading Textures from Qt Resource Files
|
||||
|
||||
The \c QOpenGLWidget interface implements methods for loading textures from QImage
|
||||
to OpenGL texture memory. We still need to use OpenGL provided functions for
|
||||
specifying the OpenGL texture unit and configuring texture filtering options.
|
||||
|
||||
\snippet cube/mainwidget.cpp 4
|
||||
|
||||
\section1 Cube Geometry
|
||||
|
||||
There are many ways to render polygons in OpenGL but the most efficient way is
|
||||
to use only triangle strip primitives and render vertices from graphics hardware
|
||||
memory. OpenGL has a mechanism to create buffer objects to this memory area and
|
||||
transfer vertex data to these buffers. In OpenGL terminology these are referred
|
||||
as Vertex Buffer Objects (VBO).
|
||||
|
||||
\image cube_faces.png Cube faces and vertices
|
||||
|
||||
This is how cube faces break down to triangles. Vertices are ordered this way
|
||||
to get vertex ordering correct using triangle strips. OpenGL determines triangle
|
||||
front and back face based on vertex ordering. By default OpenGL uses
|
||||
counter-clockwise order for front faces. This information is used by back face
|
||||
culling which improves rendering performance by not rendering back faces of the
|
||||
triangles. This way graphics pipeline can omit rendering sides of the triangle that
|
||||
aren't facing towards screen.
|
||||
|
||||
Creating vertex buffer objects and transferring data to them is quite simple using
|
||||
QOpenGLBuffer. MainWidget makes sure the GeometryEngine instance is created and
|
||||
destroyed with the OpenGL context current. This way we can use OpenGL resources
|
||||
in the constructor and perform proper cleanup in the destructor.
|
||||
|
||||
\snippet cube/geometryengine.cpp 0
|
||||
|
||||
\snippet cube/geometryengine.cpp 1
|
||||
|
||||
Drawing primitives from VBOs and telling programmable graphics pipeline how to
|
||||
locate vertex data requires few steps. First we need to bind VBOs to be used.
|
||||
After that we bind shader program attribute names and configure what
|
||||
kind of data it has in the bound VBO. Finally we'll draw triangle
|
||||
strip primitives using indices from the other VBO.
|
||||
|
||||
\snippet cube/geometryengine.cpp 2
|
||||
|
||||
\section1 Perspective Projection
|
||||
|
||||
Using \c QMatrix4x4 helper methods it's really easy to calculate perpective
|
||||
projection matrix. This matrix is used to project vertices to screen space.
|
||||
|
||||
\snippet cube/mainwidget.cpp 5
|
||||
|
||||
\section1 Orientation of the 3D Object
|
||||
|
||||
Quaternions are handy way to represent orientation of the 3D object. Quaternions
|
||||
involve quite complex mathematics but fortunately all the necessary mathematics
|
||||
behind quaternions is implemented in \c QQuaternion. That allows us to store
|
||||
cube orientation in quaternion and rotating cube around given axis is quite
|
||||
simple.
|
||||
|
||||
The following code calculates rotation axis and angular speed based on mouse events.
|
||||
|
||||
\snippet cube/mainwidget.cpp 0
|
||||
|
||||
\c QBasicTimer is used to animate scene and update cube orientation. Rotations
|
||||
can be concatenated simply by multiplying quaternions.
|
||||
|
||||
\snippet cube/mainwidget.cpp 1
|
||||
|
||||
Model-view matrix is calculated using the quaternion and by moving world by Z axis.
|
||||
This matrix is multiplied with the projection matrix to get MVP matrix for shader
|
||||
program.
|
||||
|
||||
\snippet cube/mainwidget.cpp 6
|
||||
|
||||
*/
|
19
examples/opengl/doc/src/hellogl2.qdoc
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example hellogl2
|
||||
\title Hello GL2 Example
|
||||
\ingroup examples-widgets-opengl
|
||||
|
||||
\brief The Hello GL2 example demonstrates the basic use of the OpenGL-related classes
|
||||
provided with Qt.
|
||||
|
||||
\image hellogl2-example.png
|
||||
|
||||
Qt provides the QOpenGLWidget class to enable OpenGL graphics to be rendered
|
||||
within a standard application user interface. By subclassing this class, and
|
||||
providing reimplementations of event handler functions, 3D scenes can be
|
||||
displayed on widgets that can be placed in layouts, connected to other
|
||||
objects using signals and slots, and manipulated like any other widget.
|
||||
*/
|
28
examples/opengl/doc/src/hellogles3.qdoc
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2018 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example hellogles3
|
||||
\title Hello GLES3 Example
|
||||
\ingroup examples-widgets-opengl
|
||||
|
||||
\brief The Hello GLES3 example demonstrates easy, cross-platform usage of
|
||||
OpenGL ES 3.0 functions via QOpenGLExtraFunctions in an application that
|
||||
works identically on desktop platforms with OpenGL 3.3 and mobile/embedded
|
||||
devices with OpenGL ES 3.0.
|
||||
|
||||
The code is always the same, with the exception of two places:
|
||||
\list
|
||||
\li The OpenGL context creation has to have a sufficiently high version
|
||||
number for the features that are in use.
|
||||
\li The shader code's version directive is different.
|
||||
\endlist
|
||||
|
||||
This example has no QWidget dependencies. Instead, it uses QOpenGLWindow, a
|
||||
convenience subclass of QWindow that allows easy implementation of windows
|
||||
that contain OpenGL-rendered content. In this sense it complements the
|
||||
\l{OpenGL Window Example}, which shows the implementation of an OpenGL-based
|
||||
QWindow without using the convenience subclass.
|
||||
|
||||
\image hellogles3-example.png
|
||||
*/
|
136
examples/opengl/doc/src/openglwindow.qdoc
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example openglwindow
|
||||
\title OpenGL Window Example
|
||||
|
||||
\brief This example shows how to create a minimal QWindow based application
|
||||
for the purpose of using OpenGL.
|
||||
|
||||
\image openglwindow-example.png Screenshot of the OpenGLWindow example
|
||||
|
||||
\note This is a low level example of how to use QWindow with OpenGL.
|
||||
In practice you should consider using the higher level QOpenGLWindow class.
|
||||
See the \l{Hello GLES3 Example} for a demonstration of the QOpenGLWindow
|
||||
convenience class.
|
||||
|
||||
\section1 OpenGLWindow Super Class
|
||||
|
||||
Our OpenGLWindow class acts as an API which is then subclassed to do the
|
||||
actual rendering. It has functions to make a request for render() to be
|
||||
called, either immediately with renderNow() or as soon as the event loop
|
||||
has finished processing the current batch of events with renderLater().
|
||||
The OpenGLWindow subclass can either reimplement render() for OpenGL based
|
||||
rendering, or render(QPainter *) for rendering with a QPainter. Use
|
||||
OpenGLWindow::setAnimating(true) for render() to be called at the vertical
|
||||
refresh rate, assuming vertical sync is enabled in the underlying OpenGL
|
||||
drivers.
|
||||
|
||||
In the class that does the OpenGL rendering you will typically want to
|
||||
inherit from QOpenGLFunctions, as our OpenGLWindow does, in order to get
|
||||
platform independent access to OpenGL ES 2.0 functions. By inheriting from
|
||||
QOpenGLFunctions the OpenGL functions it contains will get precedence, and
|
||||
you will not have to worry about resolving those functions if you want your
|
||||
application to work with OpenGL as well as OpenGL ES 2.0.
|
||||
|
||||
\snippet openglwindow/openglwindow.h 1
|
||||
|
||||
The window's surface type must be set to QSurface::OpenGLSurface to
|
||||
indicate that the window is to be used for OpenGL rendering and not for
|
||||
rendering raster content with QPainter using a QBackingStore.
|
||||
|
||||
\snippet openglwindow/openglwindow.cpp 1
|
||||
|
||||
Any OpenGL initialization needed can be done by overriding the initialize()
|
||||
function, which is called once before the first call to render(), with a
|
||||
valid current QOpenGLContext. As can be seen in the following code snippet,
|
||||
the default render(QPainter *) and initialize() implementations are empty,
|
||||
whereas the default render() implementation initializes a
|
||||
QOpenGLPaintDevice and then calls into render(QPainter *).
|
||||
|
||||
\snippet openglwindow/openglwindow.cpp 2
|
||||
|
||||
The renderLater() function simply calls QWindow::requestUpdate() to schedule
|
||||
an update for when the system is ready to repaint.
|
||||
|
||||
We also call renderNow() when we get an expose event. The exposeEvent() is
|
||||
the notification to the window that its exposure, meaning visibility, on
|
||||
the screen has changed. When the expose event is received you can query
|
||||
QWindow::isExposed() to find out whether or not the window is currently
|
||||
exposed. Do not render to or call QOpenGLContext::swapBuffers() on a window
|
||||
before it has received its first expose event, as before then its final
|
||||
size might be unknown, and in addition what is rendered might not even end
|
||||
up on the screen.
|
||||
|
||||
\snippet openglwindow/openglwindow.cpp 3
|
||||
|
||||
In renderNow() we return if we are not currently exposed, in which case
|
||||
rendering is delayed until we actually get an expose event. If we have not
|
||||
yet done so, we create the QOpenGLContext with the same QSurfaceFormat as
|
||||
was set on the OpenGLWindow, and call initialize() for the sake of the sub
|
||||
class, and initializeOpenGLFunctions() in order for the QOpenGLFunctions
|
||||
super class to be associated with the correct QOpenGLContext. In any case
|
||||
we make the context current by calling QOpenGLContext::makeCurrent(), call
|
||||
render() to do the actual rendering, and finally we schedule for the
|
||||
rendered contents to be made visible by calling
|
||||
QOpenGLContext::swapBuffers() with the OpenGLWindow as parameter.
|
||||
|
||||
Once the rendering of a frame using an OpenGL context is initiated by
|
||||
calling QOpenGLContext::makeCurrent(), giving the surface on which to
|
||||
render as a parameter, OpenGL commands can be issued. The commands can be
|
||||
issued either directly by including <qopengl.h>, which also includes the
|
||||
system's OpenGL headers, or as by using QOpenGLFunctions, which can
|
||||
either be inherited from for convenience, or accessed using
|
||||
QOpenGLContext::functions(). QOpenGLFunctions gives access to all the
|
||||
OpenGL ES 2.0 level OpenGL calls that are not already standard in both
|
||||
OpenGL ES 2.0 and desktop OpenGL. For more information about the OpenGL and
|
||||
OpenGL ES APIs, refer to the official \l{http://www.opengl.org/registry/}{OpenGL Registry} and
|
||||
\l {http://www.khronos.org/registry/gles/}{Khronos OpenGL ES API Registry}.
|
||||
|
||||
If animation has been enabled with OpenGLWindow::setAnimating(true), we
|
||||
call renderLater() to schedule another update request.
|
||||
|
||||
\snippet openglwindow/openglwindow.cpp 4
|
||||
|
||||
Enabling animation also schedules an update request as shown in the
|
||||
following code snippet.
|
||||
|
||||
\snippet openglwindow/openglwindow.cpp 5
|
||||
|
||||
\section1 Example OpenGL Rendering Sub Class
|
||||
|
||||
Here we sub class OpenGLWindow to show how to do OpenGL to render a
|
||||
rotating triangle. By indirectly sub classing QOpenGLFunctions we gain
|
||||
access to all OpenGL ES 2.0 level functionality.
|
||||
|
||||
\snippet openglwindow/main.cpp 1
|
||||
|
||||
In our main function we initialize QGuiApplication and instantiate our
|
||||
TriangleOpenGLWindow. We give it a QSurfaceFormat specifying that we want
|
||||
four samples of multisample antialiasing, as well as a default geometry.
|
||||
Since we want to have animation we call the above mentioned setAnimating()
|
||||
function with an argument of true.
|
||||
|
||||
\snippet openglwindow/main.cpp 2
|
||||
|
||||
The following code snippet shows the OpenGL shader program used in this
|
||||
example. The vertex and fragment shaders are relatively simple, doing
|
||||
vertex transformation and interpolated vertex coloring.
|
||||
|
||||
\snippet openglwindow/main.cpp 3
|
||||
|
||||
Here is the code that loads the shaders and initializes the shader program
|
||||
By using QOpenGLShaderProgram instead of raw OpenGL we get the convenience
|
||||
that strips out the highp, mediump, and lowp qualifiers on desktop OpenGL,
|
||||
where they are not part of the standard. We store the attribute and uniform
|
||||
locations in member variables to avoid having to do the location lookup
|
||||
each frame.
|
||||
|
||||
\snippet openglwindow/main.cpp 4
|
||||
|
||||
Finally, here is our render() function, where we use OpenGL to set up the
|
||||
viewport, clear the background, and render a rotating triangle.
|
||||
|
||||
\snippet openglwindow/main.cpp 5
|
||||
*/
|
41
examples/opengl/doc/src/stereoqopenglwidget.qdoc
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example stereoqopenglwidget
|
||||
\title QOpenGLWidget Stereoscopic Rendering Example
|
||||
|
||||
\brief This example shows how to create a minimal QOpenGLWidget based application
|
||||
with stereoscopic rendering support.
|
||||
|
||||
\image stereoexample-leftbuffer.png
|
||||
|
||||
The above image is what will be rendered to the left buffer.
|
||||
|
||||
\image stereoexample-rightbuffer.png
|
||||
|
||||
The above image is what will be rendered to the right buffer.
|
||||
|
||||
\note Support for stereoscopic rendering has certain hardware requirements, like
|
||||
your graphics card needs stereo support.
|
||||
|
||||
\section1 Setting the correct surface flag
|
||||
To enable stereoscopic rendering you need to set the flag
|
||||
QSurfaceFormat::StereoBuffers globally. Just doing it on the widget is not enough
|
||||
because of how the flag is handled internally. The safest is to do it on
|
||||
QSurfaceFormat::SetDefaultFormat prior to starting the application.
|
||||
|
||||
\snippet stereoqopenglwidget/main.cpp 1
|
||||
|
||||
\section1 Rendering twice
|
||||
After QSurfaceFormat::StereoBuffers is set, then paintGL() will be called twice,
|
||||
once for each buffer. In paintGL() you can call currentTargetBuffer() to query
|
||||
which TargetBuffer is currently active.
|
||||
|
||||
In the following snippet we slightly translate the matrix to not render the
|
||||
vertices on top of each other. This is a simple example just too see that if the
|
||||
necessary support is there, at runtime you should see two objects, one on the left
|
||||
and one on the right.
|
||||
|
||||
\snippet stereoqopenglwidget/glwidget.cpp 1
|
||||
*/
|
13
examples/opengl/doc/src/textures.qdoc
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example textures
|
||||
\title Textures Example
|
||||
\ingroup examples-widgets-opengl
|
||||
|
||||
\brief The Textures example demonstrates the use of Qt's image classes as textures in
|
||||
applications that use both OpenGL and Qt to display graphics.
|
||||
|
||||
\image textures-example.png
|
||||
*/
|
42
examples/opengl/hellogl2/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(hellogl2 LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/hellogl2")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(hellogl2
|
||||
glwidget.cpp glwidget.h
|
||||
logo.cpp logo.h
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h
|
||||
window.cpp window.h
|
||||
)
|
||||
|
||||
set_target_properties(hellogl2 PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(hellogl2 PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS hellogl2
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
257
examples/opengl/hellogl2/glwidget.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QCoreApplication>
|
||||
#include <math.h>
|
||||
|
||||
bool GLWidget::m_transparent = false;
|
||||
|
||||
GLWidget::GLWidget(QWidget *parent)
|
||||
: QOpenGLWidget(parent)
|
||||
{
|
||||
m_core = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
|
||||
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
||||
// support it, the widget will become transparent apart from the logo.
|
||||
if (m_transparent) {
|
||||
QSurfaceFormat fmt = format();
|
||||
fmt.setAlphaBufferSize(8);
|
||||
setFormat(fmt);
|
||||
}
|
||||
}
|
||||
|
||||
GLWidget::~GLWidget()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
QSize GLWidget::minimumSizeHint() const
|
||||
{
|
||||
return QSize(50, 50);
|
||||
}
|
||||
|
||||
QSize GLWidget::sizeHint() const
|
||||
{
|
||||
return QSize(400, 400);
|
||||
}
|
||||
|
||||
static void qNormalizeAngle(int &angle)
|
||||
{
|
||||
while (angle < 0)
|
||||
angle += 360 * 16;
|
||||
while (angle > 360 * 16)
|
||||
angle -= 360 * 16;
|
||||
}
|
||||
|
||||
void GLWidget::setXRotation(int angle)
|
||||
{
|
||||
qNormalizeAngle(angle);
|
||||
if (angle != m_xRot) {
|
||||
m_xRot = angle;
|
||||
emit xRotationChanged(angle);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GLWidget::setYRotation(int angle)
|
||||
{
|
||||
qNormalizeAngle(angle);
|
||||
if (angle != m_yRot) {
|
||||
m_yRot = angle;
|
||||
emit yRotationChanged(angle);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GLWidget::setZRotation(int angle)
|
||||
{
|
||||
qNormalizeAngle(angle);
|
||||
if (angle != m_zRot) {
|
||||
m_zRot = angle;
|
||||
emit zRotationChanged(angle);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GLWidget::cleanup()
|
||||
{
|
||||
if (m_program == nullptr)
|
||||
return;
|
||||
makeCurrent();
|
||||
m_logoVbo.destroy();
|
||||
delete m_program;
|
||||
m_program = nullptr;
|
||||
doneCurrent();
|
||||
QObject::disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
|
||||
}
|
||||
|
||||
static const char *vertexShaderSourceCore =
|
||||
"#version 150\n"
|
||||
"in vec4 vertex;\n"
|
||||
"in vec3 normal;\n"
|
||||
"out vec3 vert;\n"
|
||||
"out vec3 vertNormal;\n"
|
||||
"uniform mat4 projMatrix;\n"
|
||||
"uniform mat4 mvMatrix;\n"
|
||||
"uniform mat3 normalMatrix;\n"
|
||||
"void main() {\n"
|
||||
" vert = vertex.xyz;\n"
|
||||
" vertNormal = normalMatrix * normal;\n"
|
||||
" gl_Position = projMatrix * mvMatrix * vertex;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *fragmentShaderSourceCore =
|
||||
"#version 150\n"
|
||||
"in highp vec3 vert;\n"
|
||||
"in highp vec3 vertNormal;\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 color = vec3(0.39, 1.0, 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";
|
||||
|
||||
static const char *vertexShaderSource =
|
||||
"attribute vec4 vertex;\n"
|
||||
"attribute vec3 normal;\n"
|
||||
"varying vec3 vert;\n"
|
||||
"varying vec3 vertNormal;\n"
|
||||
"uniform mat4 projMatrix;\n"
|
||||
"uniform mat4 mvMatrix;\n"
|
||||
"uniform mat3 normalMatrix;\n"
|
||||
"void main() {\n"
|
||||
" vert = vertex.xyz;\n"
|
||||
" vertNormal = normalMatrix * normal;\n"
|
||||
" gl_Position = projMatrix * mvMatrix * vertex;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *fragmentShaderSource =
|
||||
"varying highp vec3 vert;\n"
|
||||
"varying highp vec3 vertNormal;\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 color = vec3(0.39, 1.0, 0.0);\n"
|
||||
" highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
|
||||
" gl_FragColor = vec4(col, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
void GLWidget::initializeGL()
|
||||
{
|
||||
// In this example the widget's corresponding top-level window can change
|
||||
// several times during the widget's lifetime. Whenever this happens, the
|
||||
// QOpenGLWidget's associated context is destroyed and a new one is created.
|
||||
// Therefore we have to be prepared to clean up the resources on the
|
||||
// aboutToBeDestroyed() signal, instead of the destructor. The emission of
|
||||
// the signal will be followed by an invocation of initializeGL() where we
|
||||
// can recreate all resources.
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);
|
||||
|
||||
initializeOpenGLFunctions();
|
||||
glClearColor(0, 0, 0, m_transparent ? 0 : 1);
|
||||
|
||||
m_program = new QOpenGLShaderProgram;
|
||||
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
|
||||
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, m_core ? fragmentShaderSourceCore : fragmentShaderSource);
|
||||
m_program->bindAttributeLocation("vertex", 0);
|
||||
m_program->bindAttributeLocation("normal", 1);
|
||||
m_program->link();
|
||||
|
||||
m_program->bind();
|
||||
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
|
||||
m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
|
||||
m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
|
||||
m_lightPosLoc = m_program->uniformLocation("lightPos");
|
||||
|
||||
// Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
|
||||
// implementations this is optional and support may not be present
|
||||
// at all. Nonetheless the below code works in all cases and makes
|
||||
// sure there is a VAO when one is needed.
|
||||
m_vao.create();
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
||||
|
||||
// Setup our vertex buffer object.
|
||||
m_logoVbo.create();
|
||||
m_logoVbo.bind();
|
||||
m_logoVbo.allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
|
||||
|
||||
// Store the vertex attribute bindings for the program.
|
||||
setupVertexAttribs();
|
||||
|
||||
// Our camera never changes in this example.
|
||||
m_camera.setToIdentity();
|
||||
m_camera.translate(0, 0, -1);
|
||||
|
||||
// Light position is fixed.
|
||||
m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
|
||||
|
||||
m_program->release();
|
||||
}
|
||||
|
||||
void GLWidget::setupVertexAttribs()
|
||||
{
|
||||
m_logoVbo.bind();
|
||||
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
|
||||
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_logoVbo.release();
|
||||
}
|
||||
|
||||
void GLWidget::paintGL()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
m_world.setToIdentity();
|
||||
m_world.rotate(180.0f - (m_xRot / 16.0f), 1, 0, 0);
|
||||
m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
||||
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
||||
m_program->bind();
|
||||
m_program->setUniformValue(m_projMatrixLoc, m_proj);
|
||||
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
|
||||
QMatrix3x3 normalMatrix = m_world.normalMatrix();
|
||||
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_logo.vertexCount());
|
||||
|
||||
m_program->release();
|
||||
}
|
||||
|
||||
void GLWidget::resizeGL(int w, int h)
|
||||
{
|
||||
m_proj.setToIdentity();
|
||||
m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
||||
}
|
||||
|
||||
void GLWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
m_lastPos = event->position().toPoint();
|
||||
}
|
||||
|
||||
void GLWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
int dx = event->position().toPoint().x() - m_lastPos.x();
|
||||
int dy = event->position().toPoint().y() - m_lastPos.y();
|
||||
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
setXRotation(m_xRot + 8 * dy);
|
||||
setYRotation(m_yRot + 8 * dx);
|
||||
} else if (event->buttons() & Qt::RightButton) {
|
||||
setXRotation(m_xRot + 8 * dy);
|
||||
setZRotation(m_zRot + 8 * dx);
|
||||
}
|
||||
m_lastPos = event->position().toPoint();
|
||||
}
|
70
examples/opengl/hellogl2/glwidget.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QMatrix4x4>
|
||||
#include "logo.h"
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||
|
||||
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GLWidget(QWidget *parent = nullptr);
|
||||
~GLWidget();
|
||||
|
||||
static bool isTransparent() { return m_transparent; }
|
||||
static void setTransparent(bool t) { m_transparent = t; }
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
QSize sizeHint() const override;
|
||||
|
||||
public slots:
|
||||
void setXRotation(int angle);
|
||||
void setYRotation(int angle);
|
||||
void setZRotation(int angle);
|
||||
void cleanup();
|
||||
|
||||
signals:
|
||||
void xRotationChanged(int angle);
|
||||
void yRotationChanged(int angle);
|
||||
void zRotationChanged(int angle);
|
||||
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
void resizeGL(int width, int height) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
void setupVertexAttribs();
|
||||
|
||||
bool m_core;
|
||||
int m_xRot = 0;
|
||||
int m_yRot = 0;
|
||||
int m_zRot = 0;
|
||||
QPoint m_lastPos;
|
||||
Logo m_logo;
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_logoVbo;
|
||||
QOpenGLShaderProgram *m_program = nullptr;
|
||||
int m_projMatrixLoc = 0;
|
||||
int m_mvMatrixLoc = 0;
|
||||
int m_normalMatrixLoc = 0;
|
||||
int m_lightPosLoc = 0;
|
||||
QMatrix4x4 m_proj;
|
||||
QMatrix4x4 m_camera;
|
||||
QMatrix4x4 m_world;
|
||||
static bool m_transparent;
|
||||
};
|
||||
|
||||
#endif
|
15
examples/opengl/hellogl2/hellogl2.pro
Normal file
@ -0,0 +1,15 @@
|
||||
HEADERS = glwidget.h \
|
||||
window.h \
|
||||
mainwindow.h \
|
||||
logo.h
|
||||
SOURCES = glwidget.cpp \
|
||||
main.cpp \
|
||||
window.cpp \
|
||||
mainwindow.cpp \
|
||||
logo.cpp
|
||||
|
||||
QT += widgets opengl openglwidgets
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/hellogl2
|
||||
INSTALLS += target
|
103
examples/opengl/hellogl2/logo.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "logo.h"
|
||||
#include <qmath.h>
|
||||
|
||||
Logo::Logo()
|
||||
{
|
||||
m_data.resize(2500 * 6);
|
||||
|
||||
const GLfloat x1 = +0.06f;
|
||||
const GLfloat y1 = -0.14f;
|
||||
const GLfloat x2 = +0.14f;
|
||||
const GLfloat y2 = -0.06f;
|
||||
const GLfloat x3 = +0.08f;
|
||||
const GLfloat y3 = +0.00f;
|
||||
const GLfloat x4 = +0.30f;
|
||||
const GLfloat y4 = +0.22f;
|
||||
|
||||
quad(x1, y1, x2, y2, y2, x2, y1, x1);
|
||||
quad(x3, y3, x4, y4, y4, x4, y3, x3);
|
||||
|
||||
extrude(x1, y1, x2, y2);
|
||||
extrude(x2, y2, y2, x2);
|
||||
extrude(y2, x2, y1, x1);
|
||||
extrude(y1, x1, x1, y1);
|
||||
extrude(x3, y3, x4, y4);
|
||||
extrude(x4, y4, y4, x4);
|
||||
extrude(y4, x4, y3, x3);
|
||||
|
||||
const int NumSectors = 100;
|
||||
|
||||
for (int i = 0; i < NumSectors; ++i) {
|
||||
GLfloat angle = (i * 2 * M_PI) / NumSectors;
|
||||
GLfloat angleSin = qSin(angle);
|
||||
GLfloat angleCos = qCos(angle);
|
||||
const GLfloat x5 = 0.30f * angleSin;
|
||||
const GLfloat y5 = 0.30f * angleCos;
|
||||
const GLfloat x6 = 0.20f * angleSin;
|
||||
const GLfloat y6 = 0.20f * angleCos;
|
||||
|
||||
angle = ((i + 1) * 2 * M_PI) / NumSectors;
|
||||
angleSin = qSin(angle);
|
||||
angleCos = qCos(angle);
|
||||
const GLfloat x7 = 0.20f * angleSin;
|
||||
const GLfloat y7 = 0.20f * angleCos;
|
||||
const GLfloat x8 = 0.30f * angleSin;
|
||||
const GLfloat y8 = 0.30f * angleCos;
|
||||
|
||||
quad(x5, y5, x6, y6, x7, y7, x8, y8);
|
||||
|
||||
extrude(x6, y6, x7, y7);
|
||||
extrude(x8, y8, x5, y5);
|
||||
}
|
||||
}
|
||||
|
||||
void Logo::add(const QVector3D &v, const QVector3D &n)
|
||||
{
|
||||
GLfloat *p = m_data.data() + m_count;
|
||||
*p++ = v.x();
|
||||
*p++ = v.y();
|
||||
*p++ = v.z();
|
||||
*p++ = n.x();
|
||||
*p++ = n.y();
|
||||
*p++ = n.z();
|
||||
m_count += 6;
|
||||
}
|
||||
|
||||
void Logo::quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4)
|
||||
{
|
||||
QVector3D n = QVector3D::normal(QVector3D(x4 - x1, y4 - y1, 0.0f), QVector3D(x2 - x1, y2 - y1, 0.0f));
|
||||
|
||||
add(QVector3D(x1, y1, -0.05f), n);
|
||||
add(QVector3D(x4, y4, -0.05f), n);
|
||||
add(QVector3D(x2, y2, -0.05f), n);
|
||||
|
||||
add(QVector3D(x3, y3, -0.05f), n);
|
||||
add(QVector3D(x2, y2, -0.05f), n);
|
||||
add(QVector3D(x4, y4, -0.05f), n);
|
||||
|
||||
n = QVector3D::normal(QVector3D(x1 - x4, y1 - y4, 0.0f), QVector3D(x2 - x4, y2 - y4, 0.0f));
|
||||
|
||||
add(QVector3D(x4, y4, 0.05f), n);
|
||||
add(QVector3D(x1, y1, 0.05f), n);
|
||||
add(QVector3D(x2, y2, 0.05f), n);
|
||||
|
||||
add(QVector3D(x2, y2, 0.05f), n);
|
||||
add(QVector3D(x3, y3, 0.05f), n);
|
||||
add(QVector3D(x4, y4, 0.05f), n);
|
||||
}
|
||||
|
||||
void Logo::extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
|
||||
{
|
||||
QVector3D n = QVector3D::normal(QVector3D(0.0f, 0.0f, -0.1f), QVector3D(x2 - x1, y2 - y1, 0.0f));
|
||||
|
||||
add(QVector3D(x1, y1, +0.05f), n);
|
||||
add(QVector3D(x1, y1, -0.05f), n);
|
||||
add(QVector3D(x2, y2, +0.05f), n);
|
||||
|
||||
add(QVector3D(x2, y2, -0.05f), n);
|
||||
add(QVector3D(x2, y2, +0.05f), n);
|
||||
add(QVector3D(x1, y1, -0.05f), n);
|
||||
}
|
28
examples/opengl/hellogl2/logo.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef LOGO_H
|
||||
#define LOGO_H
|
||||
|
||||
#include <qopengl.h>
|
||||
#include <QList>
|
||||
#include <QVector3D>
|
||||
|
||||
class Logo
|
||||
{
|
||||
public:
|
||||
Logo();
|
||||
const GLfloat *constData() const { return m_data.constData(); }
|
||||
int count() const { return m_count; }
|
||||
int vertexCount() const { return m_count / 6; }
|
||||
|
||||
private:
|
||||
void quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4);
|
||||
void extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
|
||||
void add(const QVector3D &v, const QVector3D &n);
|
||||
|
||||
QList<GLfloat> m_data;
|
||||
int m_count = 0;
|
||||
};
|
||||
|
||||
#endif // LOGO_H
|
59
examples/opengl/hellogl2/main.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QScreen>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
|
||||
#include "glwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QCoreApplication::setApplicationName("Qt Hello GL 2 Example");
|
||||
QCoreApplication::setOrganizationName("QtProject");
|
||||
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
QCommandLineOption multipleSampleOption("multisample", "Multisampling");
|
||||
parser.addOption(multipleSampleOption);
|
||||
QCommandLineOption coreProfileOption("coreprofile", "Use core profile");
|
||||
parser.addOption(coreProfileOption);
|
||||
QCommandLineOption transparentOption("transparent", "Transparent window");
|
||||
parser.addOption(transparentOption);
|
||||
|
||||
parser.process(app);
|
||||
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setDepthBufferSize(24);
|
||||
if (parser.isSet(multipleSampleOption))
|
||||
fmt.setSamples(4);
|
||||
if (parser.isSet(coreProfileOption)) {
|
||||
fmt.setVersion(3, 2);
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
}
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
MainWindow mainWindow;
|
||||
|
||||
GLWidget::setTransparent(parser.isSet(transparentOption));
|
||||
if (GLWidget::isTransparent()) {
|
||||
mainWindow.setAttribute(Qt::WA_TranslucentBackground);
|
||||
mainWindow.setAttribute(Qt::WA_NoSystemBackground, false);
|
||||
}
|
||||
mainWindow.resize(mainWindow.sizeHint());
|
||||
int desktopArea = QGuiApplication::primaryScreen()->size().width() *
|
||||
QGuiApplication::primaryScreen()->size().height();
|
||||
int widgetArea = mainWindow.width() * mainWindow.height();
|
||||
if (((float)widgetArea / (float)desktopArea) < 0.75f)
|
||||
mainWindow.show();
|
||||
else
|
||||
mainWindow.showMaximized();
|
||||
return app.exec();
|
||||
}
|
30
examples/opengl/hellogl2/mainwindow.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "window.h"
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
QMenuBar *menuBar = new QMenuBar;
|
||||
QMenu *menuWindow = menuBar->addMenu(tr("&Window"));
|
||||
QAction *addNew = new QAction(menuWindow);
|
||||
addNew->setText(tr("Add new"));
|
||||
menuWindow->addAction(addNew);
|
||||
connect(addNew, &QAction::triggered, this, &MainWindow::onAddNew);
|
||||
setMenuBar(menuBar);
|
||||
|
||||
onAddNew();
|
||||
}
|
||||
|
||||
void MainWindow::onAddNew()
|
||||
{
|
||||
if (!centralWidget())
|
||||
setCentralWidget(new Window(this));
|
||||
else
|
||||
QMessageBox::information(nullptr, tr("Cannot add new window"),
|
||||
tr("Already occupied. Undock first."));
|
||||
}
|
20
examples/opengl/hellogl2/mainwindow.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
|
||||
private slots:
|
||||
void onAddNew();
|
||||
};
|
||||
|
||||
#endif
|
97
examples/opengl/hellogl2/window.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include "window.h"
|
||||
#include "mainwindow.h"
|
||||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QPushButton>
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
|
||||
Window::Window(MainWindow *mw)
|
||||
: mainWindow(mw)
|
||||
{
|
||||
glWidget = new GLWidget;
|
||||
|
||||
xSlider = createSlider();
|
||||
ySlider = createSlider();
|
||||
zSlider = createSlider();
|
||||
|
||||
connect(xSlider, &QSlider::valueChanged, glWidget, &GLWidget::setXRotation);
|
||||
connect(glWidget, &GLWidget::xRotationChanged, xSlider, &QSlider::setValue);
|
||||
connect(ySlider, &QSlider::valueChanged, glWidget, &GLWidget::setYRotation);
|
||||
connect(glWidget, &GLWidget::yRotationChanged, ySlider, &QSlider::setValue);
|
||||
connect(zSlider, &QSlider::valueChanged, glWidget, &GLWidget::setZRotation);
|
||||
connect(glWidget, &GLWidget::zRotationChanged, zSlider, &QSlider::setValue);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *container = new QHBoxLayout;
|
||||
container->addWidget(glWidget);
|
||||
container->addWidget(xSlider);
|
||||
container->addWidget(ySlider);
|
||||
container->addWidget(zSlider);
|
||||
|
||||
QWidget *w = new QWidget;
|
||||
w->setLayout(container);
|
||||
mainLayout->addWidget(w);
|
||||
dockBtn = new QPushButton(tr("Undock"), this);
|
||||
connect(dockBtn, &QPushButton::clicked, this, &Window::dockUndock);
|
||||
mainLayout->addWidget(dockBtn);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
xSlider->setValue(15 * 16);
|
||||
ySlider->setValue(345 * 16);
|
||||
zSlider->setValue(0 * 16);
|
||||
|
||||
setWindowTitle(tr("Hello GL"));
|
||||
}
|
||||
|
||||
QSlider *Window::createSlider()
|
||||
{
|
||||
QSlider *slider = new QSlider(Qt::Vertical);
|
||||
slider->setRange(0, 360 * 16);
|
||||
slider->setSingleStep(16);
|
||||
slider->setPageStep(15 * 16);
|
||||
slider->setTickInterval(15 * 16);
|
||||
slider->setTickPosition(QSlider::TicksRight);
|
||||
return slider;
|
||||
}
|
||||
|
||||
void Window::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if (e->key() == Qt::Key_Escape)
|
||||
close();
|
||||
else
|
||||
QWidget::keyPressEvent(e);
|
||||
}
|
||||
|
||||
void Window::dockUndock()
|
||||
{
|
||||
if (parent()) {
|
||||
setParent(nullptr);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
move(QGuiApplication::primaryScreen()->size().width() / 2 - width() / 2,
|
||||
QGuiApplication::primaryScreen()->size().height() / 2 - height() / 2);
|
||||
dockBtn->setText(tr("Dock"));
|
||||
show();
|
||||
} else {
|
||||
if (!mainWindow->centralWidget()) {
|
||||
if (mainWindow->isVisible()) {
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
dockBtn->setText(tr("Undock"));
|
||||
mainWindow->setCentralWidget(this);
|
||||
} else {
|
||||
QMessageBox::information(nullptr, tr("Cannot dock"),
|
||||
tr("Main window already closed"));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::information(nullptr, tr("Cannot dock"),
|
||||
tr("Main window already occupied"));
|
||||
}
|
||||
}
|
||||
}
|
41
examples/opengl/hellogl2/window.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSlider;
|
||||
class QPushButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class GLWidget;
|
||||
class MainWindow;
|
||||
|
||||
class Window : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Window(MainWindow *mw);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void dockUndock();
|
||||
|
||||
private:
|
||||
QSlider *createSlider();
|
||||
|
||||
GLWidget *glWidget;
|
||||
QSlider *xSlider;
|
||||
QSlider *ySlider;
|
||||
QSlider *zSlider;
|
||||
QPushButton *dockBtn;
|
||||
MainWindow *mainWindow;
|
||||
};
|
||||
|
||||
#endif
|
50
examples/opengl/hellogles3/CMakeLists.txt
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(hellogles3 LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/hellogles3")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(hellogles3
|
||||
../hellogl2/logo.cpp ../hellogl2/logo.h
|
||||
glwindow.cpp glwindow.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(hellogles3 PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(hellogles3 PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(hellogles3_resource_files
|
||||
"qtlogo.png"
|
||||
)
|
||||
|
||||
qt_add_resources(hellogles3 "hellogles3"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${hellogles3_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS hellogles3
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
225
examples/opengl/hellogles3/glwindow.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwindow.h"
|
||||
#include <QImage>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QSequentialAnimationGroup>
|
||||
#include <QTimer>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
// Prepend the correct version directive to the sources. The rest is the
|
||||
// same, thanks to the common GLSL syntax.
|
||||
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");
|
||||
|
||||
// Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL.
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// Now call a function introduced in OpenGL 3.1 / OpenGL ES 3.0. We
|
||||
// requested a 3.3 or ES 3.0 context, so we know this will work.
|
||||
f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36);
|
||||
}
|
65
examples/opengl/hellogles3/glwindow.h
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWindow>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector3D>
|
||||
#include "../hellogl2/logo.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QOpenGLTexture;
|
||||
class QOpenGLShaderProgram;
|
||||
class QOpenGLBuffer;
|
||||
class QOpenGLVertexArrayObject;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class GLWindow : public QOpenGLWindow
|
||||
{
|
||||
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;
|
||||
QVector3D m_target = {0, 0, -1};
|
||||
bool m_uniformsDirty = true;
|
||||
float m_r = 0;
|
||||
float m_r2 = 0;
|
||||
};
|
||||
|
||||
#endif
|
13
examples/opengl/hellogles3/hellogles3.pro
Normal file
@ -0,0 +1,13 @@
|
||||
QT += opengl
|
||||
|
||||
HEADERS = $$PWD/glwindow.h \
|
||||
$$PWD/../hellogl2/logo.h
|
||||
|
||||
SOURCES = $$PWD/glwindow.cpp \
|
||||
$$PWD/main.cpp \
|
||||
$$PWD/../hellogl2/logo.cpp
|
||||
|
||||
RESOURCES += hellogles3.qrc
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/hellogles3
|
||||
INSTALLS += target
|
5
examples/opengl/hellogles3/hellogles3.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file>qtlogo.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
41
examples/opengl/hellogles3/main.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include "glwindow.h"
|
||||
|
||||
// This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via
|
||||
// QOpenGLExtraFunctions in an application that works identically on desktop platforms
|
||||
// with OpenGL 3.3 and mobile/embedded devices with OpenGL ES 3.0.
|
||||
|
||||
// The code is always the same, with the exception of two places: (1) the OpenGL context
|
||||
// creation has to have a sufficiently high version number for the features that are in
|
||||
// use, and (2) the shader code's version directive is different.
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setDepthBufferSize(24);
|
||||
|
||||
// Request OpenGL 3.3 core or 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.showMaximized();
|
||||
|
||||
return app.exec();
|
||||
}
|
BIN
examples/opengl/hellogles3/qtlogo.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
14
examples/opengl/opengl.pro
Normal file
@ -0,0 +1,14 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = openglwindow \
|
||||
hellogles3
|
||||
|
||||
qtHaveModule(widgets) {
|
||||
SUBDIRS += contextinfo \
|
||||
2dpainting \
|
||||
hellogl2 \
|
||||
qopenglwidget \
|
||||
cube \
|
||||
textures \
|
||||
stereoqopenglwidget
|
||||
}
|
37
examples/opengl/openglwindow/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(openglwindow LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/openglwindow")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(openglwindow
|
||||
main.cpp
|
||||
openglwindow.cpp openglwindow.h
|
||||
)
|
||||
|
||||
set_target_properties(openglwindow PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(openglwindow PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
install(TARGETS openglwindow
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
130
examples/opengl/openglwindow/main.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "openglwindow.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QScreen>
|
||||
#include <QtMath>
|
||||
|
||||
|
||||
//! [1]
|
||||
class TriangleWindow : public OpenGLWindow
|
||||
{
|
||||
public:
|
||||
using OpenGLWindow::OpenGLWindow;
|
||||
|
||||
void initialize() override;
|
||||
void render() override;
|
||||
|
||||
private:
|
||||
GLint m_posAttr = 0;
|
||||
GLint m_colAttr = 0;
|
||||
GLint m_matrixUniform = 0;
|
||||
|
||||
QOpenGLShaderProgram *m_program = nullptr;
|
||||
int m_frame = 0;
|
||||
};
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
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();
|
||||
}
|
||||
//! [2]
|
||||
|
||||
|
||||
//! [3]
|
||||
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";
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
void TriangleWindow::initialize()
|
||||
{
|
||||
m_program = new QOpenGLShaderProgram(this);
|
||||
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
|
||||
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
|
||||
m_program->link();
|
||||
m_posAttr = m_program->attributeLocation("posAttr");
|
||||
Q_ASSERT(m_posAttr != -1);
|
||||
m_colAttr = m_program->attributeLocation("colAttr");
|
||||
Q_ASSERT(m_colAttr != -1);
|
||||
m_matrixUniform = m_program->uniformLocation("matrix");
|
||||
Q_ASSERT(m_matrixUniform != -1);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
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);
|
||||
|
||||
static const GLfloat vertices[] = {
|
||||
0.0f, 0.707f,
|
||||
-0.5f, -0.5f,
|
||||
0.5f, -0.5f
|
||||
};
|
||||
|
||||
static const GLfloat colors[] = {
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
||||
glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);
|
||||
|
||||
glEnableVertexAttribArray(m_posAttr);
|
||||
glEnableVertexAttribArray(m_colAttr);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glDisableVertexAttribArray(m_colAttr);
|
||||
glDisableVertexAttribArray(m_posAttr);
|
||||
|
||||
m_program->release();
|
||||
|
||||
++m_frame;
|
||||
}
|
||||
//! [5]
|
114
examples/opengl/openglwindow/openglwindow.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "openglwindow.h"
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
|
||||
//! [1]
|
||||
OpenGLWindow::OpenGLWindow(QWindow *parent)
|
||||
: QWindow(parent)
|
||||
{
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
}
|
||||
//! [1]
|
||||
|
||||
OpenGLWindow::~OpenGLWindow()
|
||||
{
|
||||
delete m_device;
|
||||
}
|
||||
//! [2]
|
||||
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);
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
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();
|
||||
}
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
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();
|
||||
}
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
void OpenGLWindow::setAnimating(bool animating)
|
||||
{
|
||||
m_animating = animating;
|
||||
|
||||
if (animating)
|
||||
renderLater();
|
||||
}
|
||||
//! [5]
|
||||
|
48
examples/opengl/openglwindow/openglwindow.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef OPENGLWINDOW_H
|
||||
#define OPENGLWINDOW_H
|
||||
|
||||
#include <QWindow>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPainter;
|
||||
class QOpenGLContext;
|
||||
class QOpenGLPaintDevice;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
//! [1]
|
||||
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;
|
||||
};
|
||||
//! [1]
|
||||
|
||||
#endif // OPENGLWINDOW_H
|
4
examples/opengl/openglwindow/openglwindow.pri
Normal file
@ -0,0 +1,4 @@
|
||||
QT += opengl
|
||||
INCLUDEPATH += $$PWD
|
||||
SOURCES += $$PWD/openglwindow.cpp
|
||||
HEADERS += $$PWD/openglwindow.h
|
7
examples/opengl/openglwindow/openglwindow.pro
Normal file
@ -0,0 +1,7 @@
|
||||
include(openglwindow.pri)
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/openglwindow
|
||||
INSTALLS += target
|
53
examples/opengl/qopenglwidget/CMakeLists.txt
Normal file
@ -0,0 +1,53 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(qopenglwidget LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/qopenglwidget")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(qopenglwidget
|
||||
bubble.cpp bubble.h
|
||||
glwidget.cpp glwidget.h
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h
|
||||
)
|
||||
|
||||
set_target_properties(qopenglwidget PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(qopenglwidget PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(texture_resource_files
|
||||
"qt.png"
|
||||
)
|
||||
|
||||
qt6_add_resources(qopenglwidget "texture"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${texture_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS qopenglwidget
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
99
examples/opengl/qopenglwidget/bubble.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "bubble.h"
|
||||
|
||||
Bubble::Bubble(const QPointF &position, qreal radius, const QPointF &velocity)
|
||||
: position(position), vel(velocity), radius(radius)
|
||||
{
|
||||
innerColor = randomColor();
|
||||
outerColor = randomColor();
|
||||
updateBrush();
|
||||
}
|
||||
|
||||
//! [0]
|
||||
void Bubble::updateCache()
|
||||
{
|
||||
delete cache;
|
||||
cache = new QImage(qRound(radius * 2 + 2), qRound(radius * 2 + 2), QImage::Format_ARGB32_Premultiplied);
|
||||
cache->fill(0x00000000);
|
||||
QPainter p(cache);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
QPen pen(Qt::white);
|
||||
pen.setWidth(2);
|
||||
p.setPen(pen);
|
||||
p.setBrush(brush);
|
||||
p.drawEllipse(1, 1, int(2*radius), int(2*radius));
|
||||
}
|
||||
//! [0]
|
||||
|
||||
Bubble::~Bubble()
|
||||
{
|
||||
delete cache;
|
||||
}
|
||||
|
||||
void Bubble::updateBrush()
|
||||
{
|
||||
QRadialGradient gradient(QPointF(radius, radius), radius,
|
||||
QPointF(radius*0.5, radius*0.5));
|
||||
|
||||
gradient.setColorAt(0, QColor(255, 255, 255, 255));
|
||||
gradient.setColorAt(0.25, innerColor);
|
||||
gradient.setColorAt(1, outerColor);
|
||||
brush = QBrush(gradient);
|
||||
updateCache();
|
||||
}
|
||||
|
||||
//! [1]
|
||||
void Bubble::drawBubble(QPainter *painter)
|
||||
{
|
||||
painter->save();
|
||||
painter->translate(position.x() - radius, position.y() - radius);
|
||||
painter->setOpacity(0.8);
|
||||
painter->drawImage(0, 0, *cache);
|
||||
painter->restore();
|
||||
}
|
||||
//! [1]
|
||||
|
||||
QColor Bubble::randomColor()
|
||||
{
|
||||
int red = int(185 + QRandomGenerator::global()->bounded(70));
|
||||
int green = int(185 + QRandomGenerator::global()->bounded(70));
|
||||
int blue = int(205 + QRandomGenerator::global()->bounded(50));
|
||||
int alpha = int(91 + QRandomGenerator::global()->bounded(100));
|
||||
|
||||
return QColor(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
void Bubble::move(const QRect &bbox)
|
||||
{
|
||||
position += vel;
|
||||
qreal leftOverflow = position.x() - radius - bbox.left();
|
||||
qreal rightOverflow = position.x() + radius - bbox.right();
|
||||
qreal topOverflow = position.y() - radius - bbox.top();
|
||||
qreal bottomOverflow = position.y() + radius - bbox.bottom();
|
||||
|
||||
if (leftOverflow < 0.0) {
|
||||
position.setX(position.x() - 2 * leftOverflow);
|
||||
vel.setX(-vel.x());
|
||||
} else if (rightOverflow > 0.0) {
|
||||
position.setX(position.x() - 2 * rightOverflow);
|
||||
vel.setX(-vel.x());
|
||||
}
|
||||
|
||||
if (topOverflow < 0.0) {
|
||||
position.setY(position.y() - 2 * topOverflow);
|
||||
vel.setY(-vel.y());
|
||||
} else if (bottomOverflow > 0.0) {
|
||||
position.setY(position.y() - 2 * bottomOverflow);
|
||||
vel.setY(-vel.y());
|
||||
}
|
||||
}
|
||||
|
||||
QRectF Bubble::rect()
|
||||
{
|
||||
return QRectF(position.x() - radius, position.y() - radius,
|
||||
2 * radius, 2 * radius);
|
||||
}
|
39
examples/opengl/qopenglwidget/bubble.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef BUBBLE_H
|
||||
#define BUBBLE_H
|
||||
|
||||
#include <QBrush>
|
||||
#include <QColor>
|
||||
#include <QPointF>
|
||||
#include <QRect>
|
||||
#include <QRectF>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QPainter)
|
||||
|
||||
class Bubble
|
||||
{
|
||||
public:
|
||||
Bubble(const QPointF &position, qreal radius, const QPointF &velocity);
|
||||
~Bubble();
|
||||
|
||||
void drawBubble(QPainter *painter);
|
||||
void updateBrush();
|
||||
void move(const QRect &bbox);
|
||||
void updateCache();
|
||||
QRectF rect();
|
||||
|
||||
private:
|
||||
QColor randomColor();
|
||||
|
||||
QBrush brush;
|
||||
QPointF position;
|
||||
QPointF vel;
|
||||
qreal radius;
|
||||
QColor innerColor;
|
||||
QColor outerColor;
|
||||
QImage *cache = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
540
examples/opengl/qopenglwidget/glwidget.cpp
Normal file
@ -0,0 +1,540 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include <QPainter>
|
||||
#include <QPaintEngine>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QRandomGenerator>
|
||||
#include <QCoreApplication>
|
||||
#include <qmath.h>
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "bubble.h"
|
||||
|
||||
const int bubbleNum = 8;
|
||||
|
||||
#ifndef GL_SRGB8_ALPHA8
|
||||
#define GL_SRGB8_ALPHA8 0x8C43
|
||||
#endif
|
||||
|
||||
GLWidget::GLWidget(MainWindow *maybeMainWindow, const QColor &background)
|
||||
: m_mainWindow(maybeMainWindow),
|
||||
m_background(background)
|
||||
{
|
||||
setMinimumSize(300, 250);
|
||||
if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
|
||||
setTextureFormat(GL_SRGB8_ALPHA8);
|
||||
}
|
||||
|
||||
GLWidget::~GLWidget()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void GLWidget::reset()
|
||||
{
|
||||
qDeleteAll(m_bubbles);
|
||||
// Leave everything in a state suitable for a subsequent call to
|
||||
// initialize(). This matters when coming from the context's
|
||||
// aboutToBeDestroyed signal, would not matter when invoked from the
|
||||
// destructor.
|
||||
m_bubbles.clear();
|
||||
|
||||
// And now release all OpenGL resources.
|
||||
makeCurrent();
|
||||
delete m_texture;
|
||||
m_texture = nullptr;
|
||||
delete m_program1;
|
||||
m_program1 = nullptr;
|
||||
delete m_program2;
|
||||
m_program2 = nullptr;
|
||||
delete m_vshader1;
|
||||
m_vshader1 = nullptr;
|
||||
delete m_fshader1;
|
||||
m_fshader1 = nullptr;
|
||||
delete m_vshader2;
|
||||
m_vshader2 = nullptr;
|
||||
delete m_fshader2;
|
||||
m_fshader2 = nullptr;
|
||||
m_vbo1.destroy();
|
||||
m_vbo2.destroy();
|
||||
doneCurrent();
|
||||
|
||||
// We are done with the current QOpenGLContext, forget it. If there is a
|
||||
// subsequent initialize(), that will then connect to the new context.
|
||||
QObject::disconnect(m_contextWatchConnection);
|
||||
}
|
||||
|
||||
void GLWidget::setScaling(int scale)
|
||||
{
|
||||
if (scale > 30)
|
||||
m_fScale = 1 + qreal(scale - 30) / 30 * 0.25;
|
||||
else if (scale < 30)
|
||||
m_fScale = 1 - (qreal(30 - scale) / 30 * 0.25);
|
||||
else
|
||||
m_fScale = 1;
|
||||
}
|
||||
|
||||
void GLWidget::setLogo()
|
||||
{
|
||||
m_qtLogo = true;
|
||||
}
|
||||
|
||||
void GLWidget::setTexture()
|
||||
{
|
||||
m_qtLogo = false;
|
||||
}
|
||||
|
||||
void GLWidget::setShowBubbles(bool bubbles)
|
||||
{
|
||||
m_showBubbles = bubbles;
|
||||
}
|
||||
|
||||
void GLWidget::paintQtLogo()
|
||||
{
|
||||
m_program1->enableAttributeArray(m_vertexAttr1);
|
||||
m_program1->enableAttributeArray(m_normalAttr1);
|
||||
|
||||
m_vbo1.bind();
|
||||
// The data in the buffer is placed like this:
|
||||
// vertex1.x, vertex1.y, vertex1.z, normal1.x, normal1.y, normal1.z, vertex2.x, ...
|
||||
m_program1->setAttributeBuffer(m_vertexAttr1, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
|
||||
m_program1->setAttributeBuffer(m_normalAttr1, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
|
||||
m_vbo1.release();
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
|
||||
|
||||
m_program1->disableAttributeArray(m_normalAttr1);
|
||||
m_program1->disableAttributeArray(m_vertexAttr1);
|
||||
}
|
||||
|
||||
void GLWidget::paintTexturedCube()
|
||||
{
|
||||
m_texture->bind();
|
||||
|
||||
if (!m_vbo2.isCreated()) {
|
||||
static GLfloat afVertices[] = {
|
||||
-0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5,
|
||||
0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5,
|
||||
-0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5,
|
||||
0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5,
|
||||
|
||||
0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5,
|
||||
0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5,
|
||||
-0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5,
|
||||
-0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5,
|
||||
|
||||
0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
|
||||
-0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
|
||||
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
|
||||
0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5
|
||||
};
|
||||
|
||||
static GLfloat afTexCoord[] = {
|
||||
0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
|
||||
1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
|
||||
1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
|
||||
0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
|
||||
|
||||
1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
|
||||
0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
|
||||
0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
|
||||
1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
|
||||
|
||||
0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f,
|
||||
1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f,
|
||||
1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f,
|
||||
0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f
|
||||
};
|
||||
|
||||
GLfloat afNormals[] = {
|
||||
|
||||
0,0,-1, 0,0,-1, 0,0,-1,
|
||||
0,0,-1, 0,0,-1, 0,0,-1,
|
||||
0,0,1, 0,0,1, 0,0,1,
|
||||
0,0,1, 0,0,1, 0,0,1,
|
||||
|
||||
-1,0,0, -1,0,0, -1,0,0,
|
||||
-1,0,0, -1,0,0, -1,0,0,
|
||||
1,0,0, 1,0,0, 1,0,0,
|
||||
1,0,0, 1,0,0, 1,0,0,
|
||||
|
||||
0,-1,0, 0,-1,0, 0,-1,0,
|
||||
0,-1,0, 0,-1,0, 0,-1,0,
|
||||
0,1,0, 0,1,0, 0,1,0,
|
||||
0,1,0, 0,1,0, 0,1,0
|
||||
};
|
||||
|
||||
m_vbo2.create();
|
||||
m_vbo2.bind();
|
||||
m_vbo2.allocate(36 * 8 * sizeof(GLfloat));
|
||||
m_vbo2.write(0, afVertices, sizeof(afVertices));
|
||||
m_vbo2.write(sizeof(afVertices), afTexCoord, sizeof(afTexCoord));
|
||||
m_vbo2.write(sizeof(afVertices) + sizeof(afTexCoord), afNormals, sizeof(afNormals));
|
||||
m_vbo2.release();
|
||||
}
|
||||
|
||||
m_program2->setUniformValue(m_textureUniform2, 0); // use texture unit 0
|
||||
|
||||
m_program2->enableAttributeArray(m_vertexAttr2);
|
||||
m_program2->enableAttributeArray(m_normalAttr2);
|
||||
m_program2->enableAttributeArray(m_texCoordAttr2);
|
||||
|
||||
m_vbo2.bind();
|
||||
// In the buffer we first have 36 vertices (3 floats for each), then 36 texture
|
||||
// coordinates (2 floats for each), then 36 normals (3 floats for each).
|
||||
m_program2->setAttributeBuffer(m_vertexAttr2, GL_FLOAT, 0, 3);
|
||||
m_program2->setAttributeBuffer(m_texCoordAttr2, GL_FLOAT, 36 * 3 * sizeof(GLfloat), 2);
|
||||
m_program2->setAttributeBuffer(m_normalAttr2, GL_FLOAT, 36 * 5 * sizeof(GLfloat), 3);
|
||||
m_vbo2.release();
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
m_program2->disableAttributeArray(m_vertexAttr2);
|
||||
m_program2->disableAttributeArray(m_normalAttr2);
|
||||
m_program2->disableAttributeArray(m_texCoordAttr2);
|
||||
}
|
||||
|
||||
void GLWidget::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
m_texture = new QOpenGLTexture(QImage(":/qt.png"));
|
||||
|
||||
m_vshader1 = new QOpenGLShader(QOpenGLShader::Vertex);
|
||||
const char *vsrc1 =
|
||||
"attribute highp vec4 vertex;\n"
|
||||
"attribute mediump vec3 normal;\n"
|
||||
"uniform mediump mat4 matrix;\n"
|
||||
"varying mediump vec4 color;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
|
||||
" float angle = max(dot(normal, toLight), 0.0);\n"
|
||||
" vec3 col = vec3(0.40, 1.0, 0.0);\n"
|
||||
" color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
|
||||
" color = clamp(color, 0.0, 1.0);\n"
|
||||
" gl_Position = matrix * vertex;\n"
|
||||
"}\n";
|
||||
m_vshader1->compileSourceCode(vsrc1);
|
||||
|
||||
m_fshader1 = new QOpenGLShader(QOpenGLShader::Fragment);
|
||||
const char *fsrc1 =
|
||||
"varying mediump vec4 color;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_FragColor = color;\n"
|
||||
"}\n";
|
||||
m_fshader1->compileSourceCode(fsrc1);
|
||||
|
||||
m_program1 = new QOpenGLShaderProgram;
|
||||
m_program1->addShader(m_vshader1);
|
||||
m_program1->addShader(m_fshader1);
|
||||
m_program1->link();
|
||||
|
||||
m_vertexAttr1 = m_program1->attributeLocation("vertex");
|
||||
m_normalAttr1 = m_program1->attributeLocation("normal");
|
||||
m_matrixUniform1 = m_program1->uniformLocation("matrix");
|
||||
|
||||
m_vshader2 = new QOpenGLShader(QOpenGLShader::Vertex);
|
||||
const char *vsrc2 =
|
||||
"attribute highp vec4 vertex;\n"
|
||||
"attribute highp vec4 texCoord;\n"
|
||||
"attribute mediump vec3 normal;\n"
|
||||
"uniform mediump mat4 matrix;\n"
|
||||
"varying highp vec4 texc;\n"
|
||||
"varying mediump float angle;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
|
||||
" angle = max(dot(normal, toLight), 0.0);\n"
|
||||
" gl_Position = matrix * vertex;\n"
|
||||
" texc = texCoord;\n"
|
||||
"}\n";
|
||||
m_vshader2->compileSourceCode(vsrc2);
|
||||
|
||||
m_fshader2 = new QOpenGLShader(QOpenGLShader::Fragment);
|
||||
const char *fsrc2 =
|
||||
"varying highp vec4 texc;\n"
|
||||
"uniform sampler2D tex;\n"
|
||||
"varying mediump float angle;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" highp vec3 color = texture2D(tex, texc.st).rgb;\n"
|
||||
" color = color * 0.2 + color * 0.8 * angle;\n"
|
||||
" gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);\n"
|
||||
"}\n";
|
||||
m_fshader2->compileSourceCode(fsrc2);
|
||||
|
||||
m_program2 = new QOpenGLShaderProgram;
|
||||
m_program2->addShader(m_vshader2);
|
||||
m_program2->addShader(m_fshader2);
|
||||
m_program2->link();
|
||||
|
||||
m_vertexAttr2 = m_program2->attributeLocation("vertex");
|
||||
m_normalAttr2 = m_program2->attributeLocation("normal");
|
||||
m_texCoordAttr2 = m_program2->attributeLocation("texCoord");
|
||||
m_matrixUniform2 = m_program2->uniformLocation("matrix");
|
||||
m_textureUniform2 = m_program2->uniformLocation("tex");
|
||||
|
||||
m_fAngle = 0;
|
||||
m_fScale = 1;
|
||||
|
||||
createGeometry();
|
||||
|
||||
// Use a vertex buffer object. Client-side pointers are old-school and should be avoided.
|
||||
m_vbo1.create();
|
||||
m_vbo1.bind();
|
||||
// For the cube all the data belonging to the texture coordinates and
|
||||
// normals is placed separately, after the vertices. Here, for the Qt logo,
|
||||
// let's do something different and potentially more efficient: create a
|
||||
// properly interleaved data set.
|
||||
const int vertexCount = m_vertices.count();
|
||||
QList<GLfloat> buf;
|
||||
buf.resize(vertexCount * 3 * 2);
|
||||
GLfloat *p = buf.data();
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
*p++ = m_vertices[i].x();
|
||||
*p++ = m_vertices[i].y();
|
||||
*p++ = m_vertices[i].z();
|
||||
*p++ = m_normals[i].x();
|
||||
*p++ = m_normals[i].y();
|
||||
*p++ = m_normals[i].z();
|
||||
}
|
||||
m_vbo1.allocate(buf.constData(), buf.count() * sizeof(GLfloat));
|
||||
m_vbo1.release();
|
||||
|
||||
createBubbles(bubbleNum - m_bubbles.count());
|
||||
|
||||
// A well-behaved QOpenGLWidget releases OpenGL resources not only upon
|
||||
// destruction, but also when the associated OpenGL context disappears. If
|
||||
// the widget continues to exist, the context's destruction will be
|
||||
// followed by a call to initialize(). This is not strictly mandatory in
|
||||
// widgets that never change their parents.
|
||||
m_contextWatchConnection = QObject::connect(context(), &QOpenGLContext::aboutToBeDestroyed, context(), [this] { reset(); });
|
||||
}
|
||||
|
||||
void GLWidget::paintGL()
|
||||
{
|
||||
createBubbles(bubbleNum - m_bubbles.count());
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(this);
|
||||
|
||||
painter.beginNativePainting();
|
||||
|
||||
glClearColor(m_background.redF(), m_background.greenF(), m_background.blueF(), m_transparent ? 0.0f : 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glFrontFace(GL_CW);
|
||||
glCullFace(GL_FRONT);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
QMatrix4x4 modelview;
|
||||
modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
|
||||
modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
|
||||
modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
|
||||
modelview.scale(m_fScale);
|
||||
modelview.translate(0.0f, -0.2f, 0.0f);
|
||||
|
||||
if (m_qtLogo) {
|
||||
m_program1->bind();
|
||||
m_program1->setUniformValue(m_matrixUniform1, modelview);
|
||||
paintQtLogo();
|
||||
m_program1->release();
|
||||
} else {
|
||||
m_program2->bind();
|
||||
m_program2->setUniformValue(m_matrixUniform2, modelview);
|
||||
paintTexturedCube();
|
||||
m_program2->release();
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
painter.endNativePainting();
|
||||
|
||||
if (m_showBubbles) {
|
||||
for (Bubble *bubble : std::as_const(m_bubbles))
|
||||
bubble->drawBubble(&painter);
|
||||
}
|
||||
|
||||
if (const int elapsed = m_time.elapsed()) {
|
||||
QString framesPerSecond;
|
||||
framesPerSecond.setNum(m_frames /(elapsed / 1000.0), 'f', 2);
|
||||
painter.setPen(m_transparent ? Qt::black : Qt::white);
|
||||
painter.drawText(20, 40, framesPerSecond + " paintGL calls / s");
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
for (Bubble *bubble : std::as_const(m_bubbles))
|
||||
bubble->move(rect());
|
||||
|
||||
if (!(m_frames % 100)) {
|
||||
m_time.start();
|
||||
m_frames = 0;
|
||||
}
|
||||
m_fAngle += 1.0f;
|
||||
++m_frames;
|
||||
|
||||
// When requested, follow the ideal way to animate: Rely on
|
||||
// blocking swap and just schedule updates continuously.
|
||||
if (!m_mainWindow || !m_mainWindow->timerEnabled())
|
||||
update();
|
||||
}
|
||||
|
||||
void GLWidget::createBubbles(int number)
|
||||
{
|
||||
for (int i = 0; i < number; ++i) {
|
||||
QPointF position(width()*(0.1 + QRandomGenerator::global()->bounded(0.8)),
|
||||
height()*(0.1 + QRandomGenerator::global()->bounded(0.8)));
|
||||
qreal radius = qMin(width(), height())*(0.0175 + QRandomGenerator::global()->bounded(0.0875));
|
||||
QPointF velocity(width()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)),
|
||||
height()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)));
|
||||
|
||||
m_bubbles.append(new Bubble(position, radius, velocity));
|
||||
}
|
||||
}
|
||||
|
||||
void GLWidget::createGeometry()
|
||||
{
|
||||
m_vertices.clear();
|
||||
m_normals.clear();
|
||||
|
||||
qreal x1 = +0.06f;
|
||||
qreal y1 = -0.14f;
|
||||
qreal x2 = +0.14f;
|
||||
qreal y2 = -0.06f;
|
||||
qreal x3 = +0.08f;
|
||||
qreal y3 = +0.00f;
|
||||
qreal x4 = +0.30f;
|
||||
qreal y4 = +0.22f;
|
||||
|
||||
quad(x1, y1, x2, y2, y2, x2, y1, x1);
|
||||
quad(x3, y3, x4, y4, y4, x4, y3, x3);
|
||||
|
||||
extrude(x1, y1, x2, y2);
|
||||
extrude(x2, y2, y2, x2);
|
||||
extrude(y2, x2, y1, x1);
|
||||
extrude(y1, x1, x1, y1);
|
||||
extrude(x3, y3, x4, y4);
|
||||
extrude(x4, y4, y4, x4);
|
||||
extrude(y4, x4, y3, x3);
|
||||
|
||||
const int NumSectors = 100;
|
||||
const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
|
||||
|
||||
for (int i = 0; i < NumSectors; ++i) {
|
||||
qreal angle = i * sectorAngle;
|
||||
qreal x5 = 0.30 * sin(angle);
|
||||
qreal y5 = 0.30 * cos(angle);
|
||||
qreal x6 = 0.20 * sin(angle);
|
||||
qreal y6 = 0.20 * cos(angle);
|
||||
|
||||
angle += sectorAngle;
|
||||
qreal x7 = 0.20 * sin(angle);
|
||||
qreal y7 = 0.20 * cos(angle);
|
||||
qreal x8 = 0.30 * sin(angle);
|
||||
qreal y8 = 0.30 * cos(angle);
|
||||
|
||||
quad(x5, y5, x6, y6, x7, y7, x8, y8);
|
||||
|
||||
extrude(x6, y6, x7, y7);
|
||||
extrude(x8, y8, x5, y5);
|
||||
}
|
||||
|
||||
for (int i = 0;i < m_vertices.size();i++)
|
||||
m_vertices[i] *= 2.0f;
|
||||
}
|
||||
|
||||
void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
|
||||
{
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
m_vertices << QVector3D(x4, y4, -0.05f);
|
||||
|
||||
m_vertices << QVector3D(x3, y3, -0.05f);
|
||||
m_vertices << QVector3D(x4, y4, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_vertices << QVector3D(x4, y4, 0.05f);
|
||||
m_vertices << QVector3D(x2, y2, 0.05f);
|
||||
m_vertices << QVector3D(x1, y1, 0.05f);
|
||||
|
||||
m_vertices << QVector3D(x2, y2, 0.05f);
|
||||
m_vertices << QVector3D(x4, y4, 0.05f);
|
||||
m_vertices << QVector3D(x3, y3, 0.05f);
|
||||
|
||||
n = QVector3D::normal
|
||||
(QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
}
|
||||
|
||||
void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
|
||||
{
|
||||
m_vertices << QVector3D(x1, y1, +0.05f);
|
||||
m_vertices << QVector3D(x2, y2, +0.05f);
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, +0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
}
|
||||
|
||||
void GLWidget::setTransparent(bool transparent)
|
||||
{
|
||||
setAttribute(Qt::WA_AlwaysStackOnTop, transparent);
|
||||
m_transparent = transparent;
|
||||
// Call update() on the top-level window after toggling AlwayStackOnTop to make sure
|
||||
// the entire backingstore is updated accordingly.
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void GLWidget::resizeGL(int, int)
|
||||
{
|
||||
if (m_mainWindow) {
|
||||
if (!m_btn) {
|
||||
m_btn = new QPushButton("\nAdd widget\n", this);
|
||||
connect(m_btn, &QPushButton::clicked, this, [this] { m_mainWindow->addNew(); });
|
||||
}
|
||||
m_btn->move(20, 80);
|
||||
if (!m_btn2) {
|
||||
m_btn2 = new QPushButton("\nI prefer tabbed widgets\n", this);
|
||||
connect(m_btn2, &QPushButton::clicked, this, [this] { m_mainWindow->showNewWindow(); });
|
||||
}
|
||||
m_btn2->move(20, 160);
|
||||
}
|
||||
}
|
85
examples/opengl/qopenglwidget/glwidget.h
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QVector3D>
|
||||
#include <QMatrix4x4>
|
||||
#include <QElapsedTimer>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
|
||||
class Bubble;
|
||||
class MainWindow;
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShader)
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||
|
||||
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GLWidget(MainWindow *maybeMainWindow, const QColor &background);
|
||||
~GLWidget();
|
||||
|
||||
public slots:
|
||||
void setScaling(int scale);
|
||||
void setLogo();
|
||||
void setTexture();
|
||||
void setShowBubbles(bool);
|
||||
void setTransparent(bool transparent);
|
||||
|
||||
protected:
|
||||
void resizeGL(int w, int h) override;
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
|
||||
private:
|
||||
void paintTexturedCube();
|
||||
void paintQtLogo();
|
||||
void createGeometry();
|
||||
void createBubbles(int number);
|
||||
void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
|
||||
void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
|
||||
void reset();
|
||||
|
||||
MainWindow *m_mainWindow;
|
||||
qreal m_fAngle = 0;
|
||||
qreal m_fScale = 1;
|
||||
bool m_showBubbles = true;
|
||||
QList<QVector3D> m_vertices;
|
||||
QList<QVector3D> m_normals;
|
||||
bool m_qtLogo = true;
|
||||
QList<Bubble *> m_bubbles;
|
||||
int m_frames = 0;
|
||||
QElapsedTimer m_time;
|
||||
QOpenGLShader *m_vshader1 = nullptr;
|
||||
QOpenGLShader *m_fshader1 = nullptr;
|
||||
QOpenGLShader *m_vshader2 = nullptr;
|
||||
QOpenGLShader *m_fshader2 = nullptr;
|
||||
QOpenGLShaderProgram *m_program1 = nullptr;
|
||||
QOpenGLShaderProgram *m_program2 = nullptr;
|
||||
QOpenGLTexture *m_texture = nullptr;
|
||||
QOpenGLBuffer m_vbo1;
|
||||
QOpenGLBuffer m_vbo2;
|
||||
int m_vertexAttr1 = 0;
|
||||
int m_normalAttr1 = 0;
|
||||
int m_matrixUniform1 = 0;
|
||||
int m_vertexAttr2 = 0;
|
||||
int m_normalAttr2 = 0;
|
||||
int m_texCoordAttr2 = 0;
|
||||
int m_matrixUniform2 = 0;
|
||||
int m_textureUniform2 = 0;
|
||||
bool m_transparent = false;
|
||||
QPushButton *m_btn = nullptr;
|
||||
QPushButton *m_btn2 = nullptr;
|
||||
QColor m_background;
|
||||
QMetaObject::Connection m_contextWatchConnection;
|
||||
};
|
||||
|
||||
#endif
|
43
examples/opengl/qopenglwidget/main.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QColorSpace>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
Q_INIT_RESOURCE(texture);
|
||||
QApplication a( argc, argv );
|
||||
|
||||
QCoreApplication::setApplicationName("Qt QOpenGLWidget Example");
|
||||
QCoreApplication::setOrganizationName("QtProject");
|
||||
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
QCommandLineOption multipleSampleOption("multisample", "Multisampling");
|
||||
parser.addOption(multipleSampleOption);
|
||||
QCommandLineOption srgbOption("srgb", "Use sRGB Color Space");
|
||||
parser.addOption(srgbOption);
|
||||
parser.process(a);
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
format.setStencilBufferSize(8);
|
||||
if (parser.isSet(srgbOption))
|
||||
format.setColorSpace(QColorSpace::SRgb);
|
||||
if (parser.isSet(multipleSampleOption))
|
||||
format.setSamples(4);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
MainWindow mw;
|
||||
mw.resize(1280, 720);
|
||||
mw.show();
|
||||
return a.exec();
|
||||
}
|
188
examples/opengl/qopenglwidget/mainwindow.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMenuBar>
|
||||
#include <QGroupBox>
|
||||
#include <QSlider>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QRandomGenerator>
|
||||
#include <QSpinBox>
|
||||
#include <QScrollArea>
|
||||
#include <QTabWidget>
|
||||
#include <QTabBar>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "glwidget.h"
|
||||
|
||||
MainWindow::MainWindow()
|
||||
: m_nextX(1), m_nextY(1)
|
||||
{
|
||||
GLWidget *glwidget = new GLWidget(this, qRgb(20, 20, 50));
|
||||
m_glWidgets << glwidget;
|
||||
QLabel *label = new QLabel(this);
|
||||
m_timer = new QTimer(this);
|
||||
QSlider *slider = new QSlider(this);
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
|
||||
QLabel *updateLabel = new QLabel("Update interval");
|
||||
QSpinBox *updateInterval = new QSpinBox(this);
|
||||
updateInterval->setSuffix(" ms");
|
||||
updateInterval->setValue(10);
|
||||
updateInterval->setToolTip("Interval for the timer that calls update().\n"
|
||||
"Note that on most systems the swap will block to wait for vsync\n"
|
||||
"and therefore an interval < 16 ms will likely lead to a 60 FPS update rate.");
|
||||
QGroupBox *updateGroupBox = new QGroupBox(this);
|
||||
QCheckBox *timerBased = new QCheckBox("Use timer", this);
|
||||
timerBased->setChecked(false);
|
||||
timerBased->setToolTip("Toggles using a timer to trigger update().\n"
|
||||
"When not set, each paintGL() schedules the next update immediately,\n"
|
||||
"expecting the blocking swap to throttle the thread.\n"
|
||||
"This shows how unnecessary the timer is in most cases.");
|
||||
QCheckBox *transparent = new QCheckBox("Transparent background", this);
|
||||
transparent->setToolTip("Toggles Qt::WA_AlwaysStackOnTop and transparent clear color for glClear().\n"
|
||||
"Note how the button on top stacks incorrectly when enabling this.");
|
||||
QHBoxLayout *updateLayout = new QHBoxLayout;
|
||||
updateLayout->addWidget(updateLabel);
|
||||
updateLayout->addWidget(updateInterval);
|
||||
updateLayout->addWidget(timerBased);
|
||||
updateLayout->addWidget(transparent);
|
||||
updateGroupBox->setLayout(updateLayout);
|
||||
|
||||
slider->setRange(0, 50);
|
||||
slider->setSliderPosition(30);
|
||||
m_timer->setInterval(10);
|
||||
label->setText("A scrollable QOpenGLWidget");
|
||||
label->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QGroupBox * groupBox = new QGroupBox(this);
|
||||
setCentralWidget(groupBox);
|
||||
groupBox->setTitle("QOpenGLWidget Example");
|
||||
|
||||
m_layout = new QGridLayout(groupBox);
|
||||
|
||||
QScrollArea *scrollArea = new QScrollArea;
|
||||
scrollArea->setWidget(glwidget);
|
||||
|
||||
m_layout->addWidget(scrollArea,1,0,8,1);
|
||||
m_layout->addWidget(label,9,0,1,1);
|
||||
m_layout->addWidget(updateGroupBox, 10, 0, 1, 1);
|
||||
m_layout->addWidget(slider, 11,0,1,1);
|
||||
|
||||
groupBox->setLayout(m_layout);
|
||||
|
||||
|
||||
QMenu *fileMenu = menuBar()->addMenu("&File");
|
||||
fileMenu->addAction("E&xit", this, &QWidget::close);
|
||||
QMenu *showMenu = menuBar()->addMenu("&Show");
|
||||
showMenu->addAction("Show 3D Logo", glwidget, &GLWidget::setLogo);
|
||||
showMenu->addAction("Show 2D Texture", glwidget, &GLWidget::setTexture);
|
||||
QAction *showBubbles = showMenu->addAction("Show bubbles", glwidget, &GLWidget::setShowBubbles);
|
||||
showBubbles->setCheckable(true);
|
||||
showBubbles->setChecked(true);
|
||||
showMenu->addAction("Open tab window", this, &MainWindow::showNewWindow);
|
||||
QMenu *helpMenu = menuBar()->addMenu("&Help");
|
||||
helpMenu->addAction("About Qt", qApp, &QApplication::aboutQt);
|
||||
|
||||
connect(m_timer, &QTimer::timeout, glwidget, QOverload<>::of(&QWidget::update));
|
||||
|
||||
connect(slider, &QAbstractSlider::valueChanged, glwidget, &GLWidget::setScaling);
|
||||
connect(transparent, &QCheckBox::toggled, glwidget, &GLWidget::setTransparent);
|
||||
connect(updateInterval, &QSpinBox::valueChanged,
|
||||
this, &MainWindow::updateIntervalChanged);
|
||||
connect(timerBased, &QCheckBox::toggled, this, &MainWindow::timerUsageChanged);
|
||||
connect(timerBased, &QCheckBox::toggled, updateInterval, &QWidget::setEnabled);
|
||||
|
||||
if (timerBased->isChecked())
|
||||
m_timer->start();
|
||||
else
|
||||
updateInterval->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::updateIntervalChanged(int value)
|
||||
{
|
||||
m_timer->setInterval(value);
|
||||
if (m_timer->isActive())
|
||||
m_timer->start();
|
||||
}
|
||||
|
||||
void MainWindow::addNew()
|
||||
{
|
||||
if (m_nextY == 4)
|
||||
return;
|
||||
GLWidget *w = new GLWidget(nullptr, qRgb(QRandomGenerator::global()->bounded(256),
|
||||
QRandomGenerator::global()->bounded(256),
|
||||
QRandomGenerator::global()->bounded(256)));
|
||||
m_glWidgets << w;
|
||||
connect(m_timer, &QTimer::timeout, w, QOverload<>::of(&QWidget::update));
|
||||
m_layout->addWidget(w, m_nextY, m_nextX, 1, 1);
|
||||
if (m_nextX == 3) {
|
||||
m_nextX = 1;
|
||||
++m_nextY;
|
||||
} else {
|
||||
++m_nextX;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::timerUsageChanged(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
m_timer->start();
|
||||
} else {
|
||||
m_timer->stop();
|
||||
for (QOpenGLWidget *w : std::as_const(m_glWidgets))
|
||||
w->update();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
m_glWidgets[0]->setMinimumSize(size() + QSize(128, 128));
|
||||
}
|
||||
|
||||
void MainWindow::showNewWindow()
|
||||
{
|
||||
QTabWidget *tabs = new QTabWidget;
|
||||
tabs->resize(800, 600);
|
||||
|
||||
QToolButton *tb = new QToolButton;
|
||||
tb->setText(QLatin1String("+"));
|
||||
tabs->addTab(new QLabel(QLatin1String("Add OpenGL widgets with +")), QString());
|
||||
tabs->setTabEnabled(0, false);
|
||||
tabs->tabBar()->setTabButton(0, QTabBar::RightSide, tb);
|
||||
tabs->tabBar()->setTabsClosable(true);
|
||||
QObject::connect(tabs->tabBar(), &QTabBar::tabCloseRequested, tabs, [tabs](int index) {
|
||||
tabs->widget(index)->deleteLater();
|
||||
});
|
||||
|
||||
const QString msgToTopLevel = QLatin1String("Break out to top-level window");
|
||||
const QString msgFromTopLevel = QLatin1String("Move back under tab widget");
|
||||
|
||||
QObject::connect(tb, &QAbstractButton::clicked, tabs, [=] {
|
||||
GLWidget *glwidget = new GLWidget(nullptr, Qt::blue);
|
||||
glwidget->resize(tabs->size());
|
||||
glwidget->setWindowTitle(QString::asprintf("QOpenGLWidget %p", glwidget));
|
||||
|
||||
QPushButton *btn = new QPushButton(msgToTopLevel, glwidget);
|
||||
connect(btn, &QPushButton::clicked, glwidget, [=] {
|
||||
if (glwidget->parent()) {
|
||||
glwidget->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
glwidget->setParent(nullptr);
|
||||
glwidget->show();
|
||||
btn->setText(msgFromTopLevel);
|
||||
} else {
|
||||
glwidget->setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
tabs->addTab(glwidget, glwidget->windowTitle());
|
||||
btn->setText(msgToTopLevel);
|
||||
}
|
||||
});
|
||||
|
||||
tabs->setCurrentIndex(tabs->addTab(glwidget, glwidget->windowTitle()));
|
||||
});
|
||||
|
||||
tabs->setAttribute(Qt::WA_DeleteOnClose);
|
||||
tabs->show();
|
||||
}
|
39
examples/opengl/qopenglwidget/mainwindow.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QTimer>
|
||||
#include <QGridLayout>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLWidget)
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
void addNew();
|
||||
bool timerEnabled() const { return m_timer->isActive(); }
|
||||
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
public slots:
|
||||
void showNewWindow();
|
||||
|
||||
private slots:
|
||||
void updateIntervalChanged(int value);
|
||||
void timerUsageChanged(bool enabled);
|
||||
|
||||
private:
|
||||
QTimer *m_timer;
|
||||
QGridLayout *m_layout;
|
||||
int m_nextX;
|
||||
int m_nextY;
|
||||
QList<QOpenGLWidget *> m_glWidgets;
|
||||
};
|
||||
|
||||
#endif
|
15
examples/opengl/qopenglwidget/qopenglwidget.pro
Normal file
@ -0,0 +1,15 @@
|
||||
QT += widgets opengl openglwidgets
|
||||
|
||||
SOURCES += main.cpp \
|
||||
glwidget.cpp \
|
||||
mainwindow.cpp \
|
||||
bubble.cpp
|
||||
|
||||
HEADERS += glwidget.h \
|
||||
mainwindow.h \
|
||||
bubble.h
|
||||
|
||||
RESOURCES += texture.qrc
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwidget
|
||||
INSTALLS += target
|
BIN
examples/opengl/qopenglwidget/qt.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
5
examples/opengl/qopenglwidget/texture.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>qt.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
41
examples/opengl/stereoqopenglwidget/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(stereoqopenglwidget LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/stereoqopenglwidget")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_add_executable(stereoqopenglwidget
|
||||
glwidget.cpp glwidget.h
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h
|
||||
)
|
||||
|
||||
set_target_properties(stereoqopenglwidget PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(stereoqopenglwidget PUBLIC
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
Qt::OpenGL
|
||||
Qt::OpenGLWidgets
|
||||
Qt::Widgets
|
||||
)
|
||||
|
||||
|
||||
install(TARGETS stereoqopenglwidget
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
277
examples/opengl/stereoqopenglwidget/glwidget.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include <QPainter>
|
||||
#include <QPaintEngine>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QRandomGenerator>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileDialog>
|
||||
#include <qmath.h>
|
||||
|
||||
GLWidget::GLWidget(const QColor &background)
|
||||
: m_background(background)
|
||||
{
|
||||
setMinimumSize(300, 250);
|
||||
}
|
||||
|
||||
GLWidget::~GLWidget()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void GLWidget::saveImage(TargetBuffer targetBuffer)
|
||||
{
|
||||
QImage img = grabFramebuffer(targetBuffer);
|
||||
if (img.isNull()) {
|
||||
qFatal("Failed to grab framebuffer");
|
||||
}
|
||||
|
||||
const char *fn =
|
||||
targetBuffer == TargetBuffer::LeftBuffer
|
||||
? "leftBuffer.png" : "rightBuffer.png";
|
||||
|
||||
QFileDialog fd(this);
|
||||
fd.setAcceptMode(QFileDialog::AcceptSave);
|
||||
fd.setDefaultSuffix("png");
|
||||
fd.selectFile(fn);
|
||||
if (fd.exec() == QDialog::Accepted)
|
||||
img.save(fd.selectedFiles().first());
|
||||
}
|
||||
|
||||
void GLWidget::reset()
|
||||
{
|
||||
// And now release all OpenGL resources.
|
||||
makeCurrent();
|
||||
delete m_program;
|
||||
m_program = nullptr;
|
||||
delete m_vshader;
|
||||
m_vshader = nullptr;
|
||||
delete m_fshader;
|
||||
m_fshader = nullptr;
|
||||
m_vbo.destroy();
|
||||
doneCurrent();
|
||||
|
||||
// We are done with the current QOpenGLContext, forget it. If there is a
|
||||
// subsequent initialize(), that will then connect to the new context.
|
||||
QObject::disconnect(m_contextWatchConnection);
|
||||
}
|
||||
void GLWidget::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
m_vshader = new QOpenGLShader(QOpenGLShader::Vertex);
|
||||
const char *vsrc1 =
|
||||
"attribute highp vec4 vertex;\n"
|
||||
"attribute mediump vec3 normal;\n"
|
||||
"uniform mediump mat4 matrix;\n"
|
||||
"varying mediump vec4 color;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
|
||||
" float angle = max(dot(normal, toLight), 0.0);\n"
|
||||
" vec3 col = vec3(0.40, 1.0, 0.0);\n"
|
||||
" color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
|
||||
" color = clamp(color, 0.0, 1.0);\n"
|
||||
" gl_Position = matrix * vertex;\n"
|
||||
"}\n";
|
||||
m_vshader->compileSourceCode(vsrc1);
|
||||
|
||||
m_fshader = new QOpenGLShader(QOpenGLShader::Fragment);
|
||||
const char *fsrc1 =
|
||||
"varying mediump vec4 color;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_FragColor = color;\n"
|
||||
"}\n";
|
||||
m_fshader->compileSourceCode(fsrc1);
|
||||
|
||||
m_program = new QOpenGLShaderProgram;
|
||||
m_program->addShader(m_vshader);
|
||||
m_program->addShader(m_fshader);
|
||||
m_program->link();
|
||||
|
||||
|
||||
m_vertexAttr = m_program->attributeLocation("vertex");
|
||||
m_normalAttr = m_program->attributeLocation("normal");
|
||||
m_matrixUniform = m_program->uniformLocation("matrix");
|
||||
|
||||
createGeometry();
|
||||
|
||||
m_vbo.create();
|
||||
m_vbo.bind();
|
||||
const int vertexCount = m_vertices.count();
|
||||
QList<GLfloat> buf;
|
||||
buf.resize(vertexCount * 3 * 2);
|
||||
GLfloat *p = buf.data();
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
*p++ = m_vertices[i].x();
|
||||
*p++ = m_vertices[i].y();
|
||||
*p++ = m_vertices[i].z();
|
||||
*p++ = m_normals[i].x();
|
||||
*p++ = m_normals[i].y();
|
||||
*p++ = m_normals[i].z();
|
||||
}
|
||||
m_vbo.allocate(buf.constData(), (int)buf.count() * sizeof(GLfloat));
|
||||
m_vbo.release();
|
||||
|
||||
m_contextWatchConnection = QObject::connect(context(), &QOpenGLContext::aboutToBeDestroyed, context(), [this] { reset(); });
|
||||
|
||||
glFrontFace(GL_CW);
|
||||
glCullFace(GL_FRONT);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void GLWidget::paintGL()
|
||||
{
|
||||
// When QSurfaceFormat::StereoBuffers is enabled, this function is called twice.
|
||||
// Once where currentTargetBuffer() == QOpenGLWidget::LeftBuffer,
|
||||
// and once where currentTargetBuffer() == QOpenGLWidget::RightBuffer.
|
||||
|
||||
glClearColor(m_background.redF(), m_background.greenF(), m_background.blueF(), 1.0f);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//! [1]
|
||||
// Slightly translate the model, so that there's a visible difference in each buffer.
|
||||
QMatrix4x4 modelview;
|
||||
if (currentTargetBuffer() == QOpenGLWidget::LeftBuffer)
|
||||
modelview.translate(-0.4f, 0.0f, 0.0f);
|
||||
else if (currentTargetBuffer() == QOpenGLWidget::RightBuffer)
|
||||
modelview.translate(0.4f, 0.0f, 0.0f);
|
||||
//! [1]
|
||||
|
||||
m_program->bind();
|
||||
m_program->setUniformValue(m_matrixUniform, modelview);
|
||||
m_program->enableAttributeArray(m_vertexAttr);
|
||||
m_program->enableAttributeArray(m_normalAttr);
|
||||
|
||||
m_vbo.bind();
|
||||
m_program->setAttributeBuffer(m_vertexAttr, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
|
||||
m_program->setAttributeBuffer(m_normalAttr, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
|
||||
m_vbo.release();
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
|
||||
|
||||
m_program->disableAttributeArray(m_normalAttr);
|
||||
m_program->disableAttributeArray(m_vertexAttr);
|
||||
m_program->release();
|
||||
update();
|
||||
}
|
||||
|
||||
void GLWidget::createGeometry()
|
||||
{
|
||||
m_vertices.clear();
|
||||
m_normals.clear();
|
||||
|
||||
qreal x1 = +0.06f;
|
||||
qreal y1 = -0.14f;
|
||||
qreal x2 = +0.14f;
|
||||
qreal y2 = -0.06f;
|
||||
qreal x3 = +0.08f;
|
||||
qreal y3 = +0.00f;
|
||||
qreal x4 = +0.30f;
|
||||
qreal y4 = +0.22f;
|
||||
|
||||
quad(x1, y1, x2, y2, y2, x2, y1, x1);
|
||||
quad(x3, y3, x4, y4, y4, x4, y3, x3);
|
||||
|
||||
extrude(x1, y1, x2, y2);
|
||||
extrude(x2, y2, y2, x2);
|
||||
extrude(y2, x2, y1, x1);
|
||||
extrude(y1, x1, x1, y1);
|
||||
extrude(x3, y3, x4, y4);
|
||||
extrude(x4, y4, y4, x4);
|
||||
extrude(y4, x4, y3, x3);
|
||||
|
||||
const int NumSectors = 100;
|
||||
const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
|
||||
|
||||
for (int i = 0; i < NumSectors; ++i) {
|
||||
qreal angle = i * sectorAngle;
|
||||
qreal x5 = 0.30 * sin(angle);
|
||||
qreal y5 = 0.30 * cos(angle);
|
||||
qreal x6 = 0.20 * sin(angle);
|
||||
qreal y6 = 0.20 * cos(angle);
|
||||
|
||||
angle += sectorAngle;
|
||||
qreal x7 = 0.20 * sin(angle);
|
||||
qreal y7 = 0.20 * cos(angle);
|
||||
qreal x8 = 0.30 * sin(angle);
|
||||
qreal y8 = 0.30 * cos(angle);
|
||||
|
||||
quad(x5, y5, x6, y6, x7, y7, x8, y8);
|
||||
|
||||
extrude(x6, y6, x7, y7);
|
||||
extrude(x8, y8, x5, y5);
|
||||
}
|
||||
|
||||
for (int i = 0;i < m_vertices.size();i++)
|
||||
m_vertices[i] *= 2.0f;
|
||||
}
|
||||
|
||||
void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
|
||||
{
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
m_vertices << QVector3D(x4, y4, -0.05f);
|
||||
|
||||
m_vertices << QVector3D(x3, y3, -0.05f);
|
||||
m_vertices << QVector3D(x4, y4, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_vertices << QVector3D(x4, y4, 0.05f);
|
||||
m_vertices << QVector3D(x2, y2, 0.05f);
|
||||
m_vertices << QVector3D(x1, y1, 0.05f);
|
||||
|
||||
m_vertices << QVector3D(x2, y2, 0.05f);
|
||||
m_vertices << QVector3D(x4, y4, 0.05f);
|
||||
m_vertices << QVector3D(x3, y3, 0.05f);
|
||||
|
||||
n = QVector3D::normal
|
||||
(QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
}
|
||||
|
||||
void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
|
||||
{
|
||||
m_vertices << QVector3D(x1, y1, +0.05f);
|
||||
m_vertices << QVector3D(x2, y2, +0.05f);
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
|
||||
m_vertices << QVector3D(x2, y2, -0.05f);
|
||||
m_vertices << QVector3D(x1, y1, -0.05f);
|
||||
m_vertices << QVector3D(x2, y2, +0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
m_normals << n;
|
||||
}
|
53
examples/opengl/stereoqopenglwidget/glwidget.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QVector3D>
|
||||
#include <QMatrix4x4>
|
||||
#include <QElapsedTimer>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShader)
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||
|
||||
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GLWidget(const QColor &background);
|
||||
~GLWidget();
|
||||
|
||||
void saveImage(QOpenGLWidget::TargetBuffer targetBuffer);
|
||||
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
|
||||
private:
|
||||
void createGeometry();
|
||||
void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
|
||||
void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
|
||||
void reset();
|
||||
|
||||
QList<QVector3D> m_vertices;
|
||||
QList<QVector3D> m_normals;
|
||||
QOpenGLShader *m_vshader = nullptr;
|
||||
QOpenGLShader *m_fshader = nullptr;
|
||||
QOpenGLShaderProgram *m_program = nullptr;
|
||||
QOpenGLBuffer m_vbo;
|
||||
int m_vertexAttr;
|
||||
int m_normalAttr;
|
||||
int m_matrixUniform;
|
||||
QColor m_background;
|
||||
QMetaObject::Connection m_contextWatchConnection;
|
||||
};
|
||||
|
||||
#endif
|
31
examples/opengl/stereoqopenglwidget/main.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
QApplication a( argc, argv );
|
||||
|
||||
QCoreApplication::setApplicationName("Qt QOpenGLWidget Stereoscopic Rendering Example");
|
||||
QCoreApplication::setOrganizationName("QtProject");
|
||||
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
|
||||
|
||||
//! [1]
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
format.setStencilBufferSize(8);
|
||||
|
||||
// Enable stereoscopic rendering support
|
||||
format.setStereo(true);
|
||||
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
//! [1]
|
||||
|
||||
MainWindow mw;
|
||||
mw.resize(1280, 720);
|
||||
mw.show();
|
||||
return a.exec();
|
||||
}
|
25
examples/opengl/stereoqopenglwidget/mainwindow.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QMenuBar>
|
||||
#include "glwidget.h"
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
GLWidget *glwidget = new GLWidget(qRgb(20, 20, 50));
|
||||
setCentralWidget(glwidget);
|
||||
|
||||
QMenu *screenShotMenu = menuBar()->addMenu("&Screenshot");
|
||||
screenShotMenu->addAction("Left buffer", this, [glwidget](){
|
||||
glwidget->saveImage(QOpenGLWidget::LeftBuffer);
|
||||
});
|
||||
|
||||
screenShotMenu->addAction("Right buffer", this, [glwidget](){
|
||||
glwidget->saveImage(QOpenGLWidget::RightBuffer);
|
||||
});
|
||||
|
||||
QMenu *helpMenu = menuBar()->addMenu("&Help");
|
||||
helpMenu->addAction("About Qt", qApp, &QApplication::aboutQt);
|
||||
}
|
21
examples/opengl/stereoqopenglwidget/mainwindow.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QTimer>
|
||||
#include <QGridLayout>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLWidget)
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
};
|
||||
|
||||
#endif
|
11
examples/opengl/stereoqopenglwidget/stereoqopenglwidget.pro
Normal file
@ -0,0 +1,11 @@
|
||||
QT += widgets opengl openglwidgets
|
||||
|
||||
SOURCES += main.cpp \
|
||||
glwidget.cpp \
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += glwidget.h \
|
||||
mainwindow.h
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/stereoqopenglwidget
|
||||
INSTALLS += target
|
57
examples/opengl/textures/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(textures LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/textures")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(textures
|
||||
glwidget.cpp glwidget.h
|
||||
main.cpp
|
||||
window.cpp window.h
|
||||
)
|
||||
|
||||
set_target_properties(textures PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(textures PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
Qt6::OpenGLWidgets
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(textures_resource_files
|
||||
"images/side1.png"
|
||||
"images/side2.png"
|
||||
"images/side3.png"
|
||||
"images/side4.png"
|
||||
"images/side5.png"
|
||||
"images/side6.png"
|
||||
)
|
||||
|
||||
qt6_add_resources(textures "textures"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${textures_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS textures
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
173
examples/opengl/textures/glwidget.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwidget.h"
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QMouseEvent>
|
||||
|
||||
GLWidget::~GLWidget()
|
||||
{
|
||||
makeCurrent();
|
||||
vbo.destroy();
|
||||
for (int i = 0; i < 6; ++i)
|
||||
delete textures[i];
|
||||
delete program;
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
QSize GLWidget::minimumSizeHint() const
|
||||
{
|
||||
return QSize(50, 50);
|
||||
}
|
||||
|
||||
QSize GLWidget::sizeHint() const
|
||||
{
|
||||
return QSize(200, 200);
|
||||
}
|
||||
|
||||
void GLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
|
||||
{
|
||||
xRot += xAngle;
|
||||
yRot += yAngle;
|
||||
zRot += zAngle;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLWidget::setClearColor(const QColor &color)
|
||||
{
|
||||
clearColor = color;
|
||||
update();
|
||||
}
|
||||
|
||||
void GLWidget::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
makeObject();
|
||||
|
||||
#define PROGRAM_VERTEX_ATTRIBUTE 0
|
||||
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
|
||||
|
||||
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
|
||||
const char *vsrc =
|
||||
"attribute highp vec4 vertex;\n"
|
||||
"attribute mediump vec4 texCoord;\n"
|
||||
"varying mediump vec4 texc;\n"
|
||||
"uniform mediump mat4 matrix;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_Position = matrix * vertex;\n"
|
||||
" texc = texCoord;\n"
|
||||
"}\n";
|
||||
vshader->compileSourceCode(vsrc);
|
||||
|
||||
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
|
||||
const char *fsrc =
|
||||
"uniform sampler2D texture;\n"
|
||||
"varying mediump vec4 texc;\n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
" gl_FragColor = texture2D(texture, texc.st);\n"
|
||||
"}\n";
|
||||
fshader->compileSourceCode(fsrc);
|
||||
|
||||
program = new QOpenGLShaderProgram;
|
||||
program->addShader(vshader);
|
||||
program->addShader(fshader);
|
||||
program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
|
||||
program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
|
||||
program->link();
|
||||
|
||||
program->bind();
|
||||
program->setUniformValue("texture", 0);
|
||||
}
|
||||
|
||||
void GLWidget::paintGL()
|
||||
{
|
||||
glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
QMatrix4x4 m;
|
||||
m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);
|
||||
m.translate(0.0f, 0.0f, -10.0f);
|
||||
m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
|
||||
m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
|
||||
m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
vbo.bind();
|
||||
program->bind();
|
||||
program->setUniformValue("matrix", m);
|
||||
program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
|
||||
program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
|
||||
program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
|
||||
program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
textures[i]->bind();
|
||||
glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
|
||||
}
|
||||
}
|
||||
void GLWidget::resizeGL(int width, int height)
|
||||
{
|
||||
int side = qMin(width, height);
|
||||
glViewport((width - side) / 2, (height - side) / 2, side, side);
|
||||
}
|
||||
|
||||
void GLWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
lastPos = event->position().toPoint();
|
||||
}
|
||||
|
||||
void GLWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
int dx = event->position().toPoint().x() - lastPos.x();
|
||||
int dy = event->position().toPoint().y() - lastPos.y();
|
||||
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
rotateBy(8 * dy, 8 * dx, 0);
|
||||
} else if (event->buttons() & Qt::RightButton) {
|
||||
rotateBy(8 * dy, 0, 8 * dx);
|
||||
}
|
||||
lastPos = event->position().toPoint();
|
||||
}
|
||||
|
||||
void GLWidget::mouseReleaseEvent(QMouseEvent * /* event */)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void GLWidget::makeObject()
|
||||
{
|
||||
static const int coords[6][4][3] = {
|
||||
{ { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } },
|
||||
{ { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } },
|
||||
{ { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } },
|
||||
{ { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } },
|
||||
{ { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } },
|
||||
{ { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } }
|
||||
};
|
||||
|
||||
for (int j = 0; j < 6; ++j)
|
||||
textures[j] = new QOpenGLTexture(QImage(QString(":/images/side%1.png").arg(j + 1)).mirrored());
|
||||
|
||||
QList<GLfloat> vertData;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
// vertex position
|
||||
vertData.append(0.2 * coords[i][j][0]);
|
||||
vertData.append(0.2 * coords[i][j][1]);
|
||||
vertData.append(0.2 * coords[i][j][2]);
|
||||
// texture coordinate
|
||||
vertData.append(j == 0 || j == 3);
|
||||
vertData.append(j == 0 || j == 1);
|
||||
}
|
||||
}
|
||||
|
||||
vbo.create();
|
||||
vbo.bind();
|
||||
vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));
|
||||
}
|
51
examples/opengl/textures/glwidget.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
|
||||
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
|
||||
|
||||
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QOpenGLWidget::QOpenGLWidget;
|
||||
~GLWidget();
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
QSize sizeHint() const override;
|
||||
void rotateBy(int xAngle, int yAngle, int zAngle);
|
||||
void setClearColor(const QColor &color);
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
void resizeGL(int width, int height) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
void makeObject();
|
||||
|
||||
QColor clearColor = Qt::black;
|
||||
QPoint lastPos;
|
||||
int xRot = 0;
|
||||
int yRot = 0;
|
||||
int zRot = 0;
|
||||
QOpenGLTexture *textures[6] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
QOpenGLShaderProgram *program = nullptr;
|
||||
QOpenGLBuffer vbo;
|
||||
};
|
||||
|
||||
#endif
|
BIN
examples/opengl/textures/images/side1.png
Normal file
After Width: | Height: | Size: 935 B |
BIN
examples/opengl/textures/images/side2.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
examples/opengl/textures/images/side3.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
examples/opengl/textures/images/side4.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/opengl/textures/images/side5.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
examples/opengl/textures/images/side6.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
22
examples/opengl/textures/main.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE(textures);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
Window window;
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|