关于C#:QOpenGLWidget的resizeGL不是调用glViewport的地方吗?

QOpenGLWidget's resizeGL is NOT the place to call glViewport?

我正在尝试新的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被覆盖在resizeGLpaintGL之间。怎么回事,如何解决?我必须在paintGL函数中诉诸视口转换吗?

文档中列出的QGLWidget和QOpenGLWidgets之间的区别之一是,后者将渲染到帧缓冲区,而不是直接渲染到屏幕。这可以把握解释的关键吗?

以防万一,我附上完整的代码以供参考。

// triangle.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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

// triangle.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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());
    }
}

//顶点着色器

1
2
3
4
5
6
7
8
9
10
11
#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;
}

//片段着色器

1
2
3
4
5
6
7
#version 330
out vec4 fragColor;
smooth in vec4 theColor;
void main()
{
    fragColor = theColor;
}


So it seems that the glViewport is overridden somewhere between resizeGL and paintGL. What is going on and how do I fix it? Do I have to resort to doing viewport transformations in my paintGL function?

Qt5可以将OpenGL用于其自己的图形。同样,作为QOpenGLWindow子级的小部件的内容也呈现给FBO。因此,这意味着在代码和Qt的功能之间进行了许多glViewport调用。

When I actually add a call to glViewport in my resizeGL function, it is apparently ignored (a€|)

是的。您的期望到底是什么?为了使OpenGL程序更强大,唯一调用glViewport的有效位置是在绘图代码中。在那里的每个教程,在窗口调整大小处理程序中放置glViewport都是错误的,应该将其刻录。

When I call glViewport(0, 0, width()/2, height()/2) in paingGL function, the rendering is as expected

是的,这就是您应该使用它的方式。