Why does QMatrix4x4::lookAt() result in a upside down camera

4.9k views Asked by At

I have a got a simple OpenGL program which sets up the camera as follows :

void
SimRenderer::render() {
glDepthMask(true);

glClearColor(0.5f, 0.5f, 0.7f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

glFrontFace(GL_CW);
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);

QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;

QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(mAlpha, 0, 1, 0); // mAlpha = 25
cameraTransformation.rotate(mBeta, 1, 0, 0);  // mBeta = 25

QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, mDistance);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);

vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection);

mProgram.bind();
mProgram.setUniformValue(mMatrixUniformLoc, mProjMatrix *  vMatrix * mMatrix );

// render a grid....
}

But the result is an upside down camera !!

Upside down camera !1

When I change the view matrix to be set up as: QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, -1, 0);

It works ! But why should I need to set my up direction as negative Y when my real up direction is positive Y ?

Complete class here : https://code.google.com/p/rapid-concepts/source/browse/trunk/simviewer/simrenderer.cpp

Other info: I am rendering to a QQuickFramebufferObject which binds a FBO to a widgets surface before calling the rendering function. Dont think that would be an issue but anyway. And this is not a texturing issue at all, there arent any textures to be flipped etc. Seems the camera is interpreting the up direction in the opposite way !!

http://doc.qt.digia.com/qt-maemo/qmatrix4x4.html#lookAt


Update :

So since using lookat and cameraTransformations both together may not work I am trying :

QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;

QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(mAlpha, 0, 1, 0); // 25
cameraTransformation.rotate(mBeta, 1, 0, 0);  // 25

cameraTransformation.translate(0, 0, mDistance);  

vMatrix = cameraTransformation.inverted();

That produces exactly the same result :)

I think the camera up axis needs to be accounted for in some way.

2

There are 2 answers

1
Jherico On

Typically this effect is caused by one of several things.

  • Mixing up radians and degrees
  • Forgetting to set the modelview matrix to the inverse of the camera transform
  • Screwing up the inputs to lookat

I suspect the issue with this is the last.

QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);

Why are you multiplying the up vector by this transformation? I can understand multiplying the distance, so that the camera position is transformed, rotating the up axis sent to a lookat function will result in weirdness I suspect.

Generally, doing transforms using a camera matrix AND using lookat is a little odd. If you already have a camera matrix with the proper rotation, you can just translate that matrix by the distance required, expressed as a Z vector of the appropriate length, probably QVector3D(0, 0, mDistance), and then setting the view matrix to the inverse of the camera matrix:

vMatrix = cameraTransformation.inverted();
0
Aetf On

It is actually not the camera that upside down but the texture was rendered to QML surface upside down. That is really confusing because you do get the correct direction (Y up) if you are using widget based stacks (QOpenGLWidget) or simply QOpenGLWindow.

Basically the same as this question. some explanation can be found on the forum or in the bug tracker.

I think the best solution is the one in bug tracker which requires no additional transformation on either the QML item or in matrix: overriding updatePaintNode to setTextureCoordinatesTransform to vertically mirrored.

QSGNode *MyQQuickFramebufferObject::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData)
{
    if (!node) {
        node = QQuickFramebufferObject::updatePaintNode(node, nodeData);
        QSGSimpleTextureNode *n = static_cast<QSGSimpleTextureNode *>(node);
        if (n)
            n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
        return node;
    }
    return QQuickFramebufferObject::updatePaintNode(node, nodeData);
}