QOpenGLWidget的resizeGL不是调用glViewport的地方吗?
Posted
技术标签:
【中文标题】QOpenGLWidget的resizeGL不是调用glViewport的地方吗?【英文标题】:QOpenGLWidget's resizeGL is NOT the place to call glViewport? 【发布时间】:2015-10-10 21:33:21 【问题描述】:我正在试验新的 QOpenGLWidget 类(请注意,这 不是 QGLWidget 类)。
我正在画一个三角形。我有一个简单的顶点着色器,它接收剪辑空间中的坐标,因此不涉及矩阵或投影。其中一个顶点的坐标为-1, -1, 0, 1
,另一个顶点的坐标为1, 1, 0, 1
。
当我没有调用 glViewport 时,程序呈现为好像我在我的 resizeGL 函数中调用 glViewport(0, 0, w, h);
,但我不是。也就是说,无论我如何调整窗口大小,三角形的两个顶点都附加到窗口的左下角和右上角。
当我在 resizeGL
函数中实际添加对 glViewport
的调用时,它显然被忽略了 - 如果我传递 w/2、h/2 或任何其他值都没关系,渲染与调用glViewport(0, 0, w, h);
时完全相同(例如,在glViewport(0, 0, w/2, h/2);
的情况下,我希望三角形出现在窗口的左下角)
当我在paingGL
函数中调用glViewport(0, 0, width()/2, height()/2)
时,呈现如预期的那样 - 一切都绘制在窗口的左下角。
因此,glViewport 似乎在resizeGL
和paintGL
之间的某个位置被覆盖。发生了什么事,我该如何解决?我是否必须在 paintGL
函数中进行视口转换?
文档中列出的 QGLWidget 和 QOpenGLWidgets 之间的区别之一是后者渲染到帧缓冲区而不是直接渲染到屏幕。这是否是解释的关键?
以防万一,我附上完整的代码以供参考。
//三角形.h
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
class Triangle
public:
Triangle();
void render();
void create();
private:
QOpenGLBuffer position_vbo;
QOpenGLFunctions *glFuncs;
;
#endif // TRIANGLE_H
//三角形.cpp
#include "triangle.h"
Triangle::Triangle()
:position_vbo(QOpenGLBuffer::VertexBuffer)
void Triangle::create()
glFuncs = QOpenGLContext::currentContext()->functions();
position_vbo.create();
float val[] =
-1.0f, -1.0f, 0.0f, 1.0f,
0.0f, -0.366f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
;
position_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
position_vbo.bind();
position_vbo.allocate(val, sizeof(val));
position_vbo.release();
void Triangle::render()
position_vbo.bind();
glFuncs->glEnableVertexAttribArray(0);
glFuncs->glEnableVertexAttribArray(1);
glFuncs->glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glFuncs->glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)(3*4*sizeof(float)));
glFuncs->glDrawArrays(GL_TRIANGLES, 0, 3);
glFuncs->glDisableVertexAttribArray(0);
glFuncs->glDisableVertexAttribArray(1);
position_vbo.release();
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include "triangle.h"
class Widget : public QOpenGLWidget
, protected QOpenGLFunctions
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void resizeGL(int w, int h) override;
private:
QOpenGLShaderProgram* program;
Triangle t;
;
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include <exception>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QOpenGLWidget(parent)
Widget::~Widget()
void Widget::initializeGL()
initializeOpenGLFunctions();
program = new QOpenGLShaderProgram(this);
if(!program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertexshader.vert"))
throw std::exception(("Vertex Shader compilation error: " + program->log()).toLocal8Bit().constData());
if(!program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragmentshader.frag"))
throw std::exception(("Fragment Shader compilation error: " + program->log()).toLocal8Bit().constData());
if(!program->link())
throw std::exception(("Program Link error: " + program->log()).toLocal8Bit().constData());
t.create();
void Widget::paintGL()
glClearColor(0.f, 0.15f, 0.05f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
//glViewport(0, 0, width()/2, height()/2); //works!!
program->bind();
t.render();
program->release();
void Widget::resizeGL(int w, int h)
glViewport(0, 0, w/2, h/2); //doesn't work
//main.cpp
#include "widget.h"
#include <exception>
#include <QApplication>
#include <QMessageBox>
int main(int argc, char *argv[])
QApplication a(argc, argv);
try
Widget w;
w.show();
return a.exec();
catch(std::exception const & e)
QMessageBox::warning(nullptr, "Error", e.what());
//顶点着色器
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
smooth out vec4 theColor;
void main()
gl_Position = position;
theColor = color;
//片段着色器
#version 330
out vec4 fragColor;
smooth in vec4 theColor;
void main()
fragColor = theColor;
【问题讨论】:
据我所知,QOpenGLWidget 绑定了内部帧缓冲区(id=1),并且还在调用paintGL 之前将视口设置为帧缓冲区大小。 【参考方案1】:所以看起来 glViewport 在 resizeGL 和 paintGL 之间的某个地方被覆盖了。发生了什么事,我该如何解决?我是否必须在我的paintGL 函数中进行视口转换?
Qt5 可以使用 OpenGL 进行自己的绘图。此外,作为 QOpenGLWindow 的子级的小部件的内容也会呈现给 FBO。这意味着,在您的代码和 Qt 所做的事情之间进行了大量 glViewport 调用。
当我在我的 resizeGL 函数中实际添加对 glViewport 的调用时,它显然被忽略了 (...)
是的。你的期望究竟是什么?调用glViewport
以使OpenGL 程序健壮的唯一有效位置是在绘图代码中。将glViewport
放在窗口调整大小处理程序中的每个教程都是错误的,应该被烧掉。
当我在 paingGL 函数中调用 glViewport(0, 0, width()/2, height()/2) 时,渲染如预期
是的,这就是你应该使用它的方式。
【讨论】:
好吧,我很高兴在paintGL 中调用glViewport 很好。我认为在每一帧上都这样做不是一个好主意,因为看起来我见过的所有教程都应该被烧掉。那你是说在paintGL中也要设置透视矩阵? @ArmenTsirunyan:是的,确实如此,您应该始终为每一帧执行完整的状态设置。您必须意识到,设置矩阵本身或对其执行一些操作不会产生任何 GPU 成本(它只消耗一点 CPU 时间)。当涉及到实际绘图时,矩阵无论如何都会加载到 GPU 统一寄存器中。 @ArmenTsirunyan 我同意 datenwolf 的观点:您必须与 Qt 本身共享上下文(除非您创建自己的共享上下文)。个人而言,我在paintGL 中设置了所有内容(视口、矩阵、混合选项、纹理选项……),没有性能损失。请记住,OpenGL 是一个状态机,开发人员很少恢复状态,因为他们在函数的入口点找到了状态! 一般来说恢复状态其实很痛苦。您不知道其他代码已启用、绑定等内容,特别是如果它与一些甚至不知道的扩展有关。因此,一般来说,使用您可以完全控制的自己的上下文会更加健壮,而不是希望其他代码没有破坏默认代码。以上是关于QOpenGLWidget的resizeGL不是调用glViewport的地方吗?的主要内容,如果未能解决你的问题,请参考以下文章