Search code examples
qtopenglorthographic

Problems keeping orthographic projection proportional using Qt/OpenGL


I am having a difficult time understanding what values I should use in the call I make to QMatrix4x4::ortho(left, right, bottom, top, near, far).

Specifically, I don't understand the documentation for the values for left,right,bottom, and top. I have a working demo that can draw the OpenGL monkey with a projection matrix. I set up the demo so that when I hit 'O' on the keyboard, it switches the projection matrix from perspective to orthographic. The perspective projection works well in that it keeps the model's aspect ratio constant (i.e. it doesn't stretch in width or height). Here's the function that gets called when either the 'O' or 'P' key gets called and updates m_proj. It's a bit of a hot mess as you can see I've tried a bunch of ideas and none of them really works the way I would like.

Thanks for any insight that would help me understand this. Other helpful details: my view eye is at z=2 facing center (0,0,0) with up being (0,1,0).

void AppGLScene::setProjectionMatrix(void)
{
    m_projectionMatrix.setToIdentity();
    float windowWidth = rect().width();
    float windowHeight = rect().height();
    float left, right, bottom, top;
    float aratio = (float) windowWidth / (float) windowHeight;
    qDebug() << "win wid, win hei" << windowWidth << windowHeight;

    // I modify the vertical FOV in an attempt to keep the size of the
    // model the same as the vertical size of the window changes.
    //
    float vFov = 90 * ((float)windowHeight / m_initialWinHeight);
    qDebug() << "vFov" << vFov;

    switch (m_proj)
    {
    case PROJ_PERSP:
        m_projectionMatrix.perspective(vFov, qreal(windowWidth)/qreal(windowHeight), 0.5, 40);
        break;
    case PROJ_ORTHO:
    default:
//        left = rect().x();
//        right = rect().x() + rect().width();
//        bottom = rect().y();
//        top = rect().y() + rect().height();
        if (windowWidth > windowHeight)
        {
            left = -(3.0 - ((float)windowHeight/(float)windowWidth));
            right = -left;
            bottom = -3.0;
            top = 3.0;
        }
        else
        {
            left = -3.0;
            right = 3.0;
            bottom = -(3.0 - ((float)windowWidth/(float)windowHeight));
            top = -bottom;
        }

        qDebug() << "l r b t = " << left << right << bottom << top;
        m_projectionMatrix.ortho(left, right, bottom, top, 0.5, 40);
//        m_projectionMatrix.ortho(-3.0, 3.0, -3.0, 3.0, 0.5, 40);
//        m_projectionMatrix.ortho(-aratio, aratio, -aratio, aratio, 0.5, 40);
        break;
    }
}

Solution

  • To avoid stretching your objects in either direction, you need to have (right - left) / (top - bottom) match the aspect ratio of the window. Which in your case you can ensure by having right be the value of top multiplied by the aspect ratio.

    It looks like you want to use the range [-3.0, 3.0] for the shorter window dimension, and adjust the longer one accordingly. The above then translates into:

    if (windowWidth > windowHeight)
    {
        top = 3.0f;
        bottom = -top;
        right = top * aratio;
        left = -right;
    }
    else
    {
        right = 3.0f;
        left = -right;
        top = right / aratio;
        bottom = -top;
    }
    

    Note that right / top = aratio for both cases.