8

I put glViewport in QOpenGLWidget::resizeGL overwritted virtual function, which did get called and set viewport only use part of widget space. But it didn't have any effect, content still draw to full-size of widget. Did I missing anything?

Here is my code,

mywidget.h:

class MyWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget* parent = NULL);

protected:
    virtual void initializeGL();
    virtual void paintGL();
    virtual void resizeGL(int w, int h);

private:
    QOpenGLShaderProgram program;
};

and mywidget.cpp:

MyWidget::MyWidget(QWidget* parent) : QOpenGLWidget(parent)
{
}

static const GLfloat squareVertices[] = {
    -1.0f, -1.0f,
     1.0f, -1.0f,
    -1.0f,  1.0f
};

void MyWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    program.addShaderFromSourceCode(QOpenGLShader::Vertex,
        "attribute highp vec4 vec;\n"
        "void main(void)\n"
        "{\n"
        "    gl_Position = vec;\n"
        "}");
    program.addShaderFromSourceCode(QOpenGLShader::Fragment,
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
        "}");
    program.link();
    program.bind();

    program.enableAttributeArray(0);
    program.setAttributeArray(0, squareVertices, 2);
}

void MyWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT);

    //glViewport(0, 0, width()/2, height()/2);
    // if I put glViewport here, everything work as intended,
    // but we shouldn't need to set viewport everytime render a frame

    program.bind();
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
}

void MyWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w/2, h/2);
    // this line didn't have any effect, opengl still draw using full widget space
}
user3724853
  • 547
  • 3
  • 10
  • I don't think you can do this that way. glViewport only handles the affine transformation from normalized device space to screen space. If you want to render just in a part of the GL window, you will have to draw a quad with a texture of the actual rendering. This would be fairly complicated and not really efficient, so it would help if you just explained, why you want to render only to a part of the window. Maybe there's a better solution for what you want to do! See also https://www.khronos.org/opengles/sdk/docs/man/xhtml/glViewport.xml for the description of what exactly glViewport does. – Martin Hennig Oct 13 '15 at 19:59

2 Answers2

2

glViewport should be called in the drawing code, i.e. the paintGL handler, not somewhere else. Having the glViewport call in the window reshape handler is a bad anti-pattern that's just too far spread in OpenGL tutorials; it doesn't belong there.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Calculate and reset glViewport every frame in paintGL handler seems a big waste to me, since glViewport only change when window resize (resizeGL handler) – user3724853 Dec 28 '17 at 01:43
  • @user3724853: In many programs the viewport is changes several times throughout rendering a single frame. For example every time an image is rendered to a texture, or the view is split in several panes, etc. Setting the viewport is actually so cheap, that in other graphics APIs, like Vulkan, setting the viewport is considered as an integral part of rendering a frame. Calling glViewport ss neither wasteful nor inefficient and in fact the only sane way to go about this. The same goes with setting the projection matrices BTW. *Say it with me:* *"I'll call glViewport at the start of every frame."* – datenwolf Dec 28 '17 at 13:33
0

Try following,

void MyWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    GLdouble dWt;
    GLdouble dHt;

    if(w > h) {
        dHt = 1;
        dWt = ((double)w) / h;
    } else {
        dHt = ((double)h) / w;
        dWt = 1;
    }

    if(bPerspective) {
        glFrustum(-dWt, dWt, -dHt, dHt, 5, 100);
    } else {
        glOrtho(-dWt, dWt, -dHt, dHt, 5.0, 100.0);
    }

    gluLookAt(5.0, 2.0, -10.0,
              0.0, 0.0, 0.0,
              0.0, 1.0, 0.0);

    glMatrixMode(GL_MODELVIEW);
}
Milan Dhameliya
  • 129
  • 1
  • 10
  • I am using shader, so MatrixModes has no meaning here (I am not using it). glViewport setting is outside of shader as far as I know, but it seems Qt's implement didn't take it during resizeGL event? – user3724853 Jul 30 '15 at 01:40
  • When I rolled my own 2D OpenGL framework I was enforcing glViewPort before onPaint so if you have source code take a look at what happens before onPaint. In any case glViewPort is just a convenience method to affect internal transformation matrices. so there is some merit to trying above approach and in particular playing with glMatrixMode before glViewport. Finally if you are writing your shader maybe you have take these matrices into consieration – Amer Agovic Oct 17 '15 at 14:08