我在Qopenglwidget中实现滚动时遇到了困难。需要明确的是,我希望滚动条能更改OpenGL小部件中呈现的内容,而不是滚动窗口小部件本身。基于这个论坛的答案,我一直在尝试实现QAbstractScrollArea
的子类。我发现,当将OpenGL小部件设置为Qabstractscrollarea的视口时,没有什么都没有渲染了,只有一个黑屏。
为了说明这一点,我创建了一个基于QT中分布的hellogl2
示例项目的基本示例。
在mainwindow.cpp
中,当将Glwidget设置为画布的视口时,不会发生渲染。
m_scrollCanvas->setViewport(m_glWidget);
但是,当该行评论出来时,显示了OpenGL小部件。
有人知道为什么将OpenGl widet设置为QAbstractScrollArea
的视口?
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "scrollcanvas.h"
#include "glwidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_scrollCanvas = new ScrollCanvas(this);
m_glWidget = new GLWidget(m_scrollCanvas);
m_scrollCanvas->setViewport(m_glWidget);
setCentralWidget(m_scrollCanvas);
}
MainWindow::~MainWindow()
{
}
mainWindow.h
#include <QMainWindow>
#include "glwidget.h"
#include "scrollcanvas.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
ScrollCanvas * m_scrollCanvas;
GLWidget * m_glWidget;
};
glwidget.h
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <qopengl.h>
#include <QVector>
#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);
QVector<GLfloat> m_data;
int m_count;
};
class GLWidget : public QOpenGLWidget
{
public:
GLWidget(QWidget * parent);
~GLWidget();
protected:
void resizeGL(int w, int h);
void paintGL();
void initializeGL();
private:
void setupVertexAttribs();
Logo m_logo;
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_logoVbo;
QOpenGLShaderProgram *m_program;
int m_projMatrixLoc;
int m_mvMatrixLoc;
int m_normalMatrixLoc;
int m_lightPosLoc;
QMatrix4x4 m_proj;
QMatrix4x4 m_camera;
QMatrix4x4 m_world;
};
glwidget.cpp
#include "glwidget.h"
GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent), m_program(0)
{
}
GLWidget::~GLWidget()
{
m_logoVbo.destroy();
delete m_program;
m_program = 0;
}
static const char *vertexShaderSourceCore =
"#version 150n"
"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 150n"
"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::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 - (0 / 16.0f), 1, 0, 0);
m_world.rotate(0 / 16.0f, 0, 1, 0);
m_world.rotate(0 / 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::initializeGL()
{
glClearColor(0, 0, 0, 1);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, 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::resizeGL(int w, int h)
{
m_proj.setToIdentity();
m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}
void GLWidget::setupVertexAttribs()
{
m_logoVbo.bind();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
m_logoVbo.release();
}
#include <qmath.h>
Logo::Logo()
: m_count(0)
{
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);
}
scrollcanvas.h
#include <QAbstractScrollArea>
class ScrollCanvas : public QAbstractScrollArea
{
public:
ScrollCanvas(QWidget * parent) : QAbstractScrollArea(parent){}
~ScrollCanvas(){}
};
解决方案最终非常简单。首先,我使卷轴成为Glwidget的朋友类,以使我能够访问受保护的功能。然后,在实施卷轴上,我超载了 paintEvent
和 resizeEvent
。
void ScrollCanvas::resizeEvent(QResizeEvent * event)
{
GLWidget * glw = dynamic_cast<GLWidget *>(viewport());
glw->resizeEvent(event);
}
void ScrollCanvas::paintEvent(QPaintEvent *event)
{
GLWidget * glw = dynamic_cast<GLWidget *>(viewport());
glw->paintEvent(event);
}