坐标变换和投影问题

Coordinate transformations and projection issues

我一直在尝试将我的鼠标坐标转换为 OpenGL 场景中的 3D space 坐标。

目前,我的投影有点乱(我认为),当我在场景中移动时,它似乎没有完全考虑到我的 "camera"。为了检查这一点,我画了一条线。

我的调整大小功能:

    void oglWidget::resizeGL(int width, int height)
    {
        if (height == 0) {
            height = 1;
        }
        pMatrix.setToIdentity();
        pMatrix.perspective(fov, (float) width / (float) height, -1, 1);
        glViewport(0, 0, width, height);
    }

我的渲染函数(paintGl())如下:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;

QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(alpha, 0, 1, 0);
cameraTransformation.rotate(beta, 1, 0, 0);

QVector3D cameraPosition = cameraTransformation * QVector3D(camX, camY, distance);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);
vMatrix.lookAt(cameraPosition, QVector3D(camX, camY, 0), cameraUpDirection);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();    
gluLookAt(cameraPosition.x(), cameraPosition.y(), cameraPosition.z(), camX, camY, 0, cameraUpDirection.x(), cameraUpDirection.y(), cameraUpDirection.z());

shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
shaderProgram.setUniformValue("texture", 0);


for (int x = 0; x < tileCount; x++)
{
    shaderProgram.setAttributeArray("vertex", tiles[x]->vertices.constData());
    shaderProgram.enableAttributeArray("vertex");
    shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
    shaderProgram.enableAttributeArray("textureCoordinate");
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, tiles[x]->image.width(), tiles[x]->image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tiles[x]->image.bits());
    glDrawArrays(GL_TRIANGLES, 0, tiles[x]->vertices.size());
}
shaderProgram.release();

并创建我的射线:

GLdouble modelViewMatrix[16];
GLdouble projectionMatrix[16];
GLint viewport[4];
GLfloat winX, winY, winZ;
glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
glGetIntegerv(GL_VIEWPORT, viewport);

winX = (float)x;
winY = (float)viewport[3] - (float)y;
glReadPixels( winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );

GLdouble nearPlaneLocation[3];
gluUnProject(winX, winY, 0, modelViewMatrix, projectionMatrix,
             viewport, &nearPlaneLocation[0], &nearPlaneLocation[1],
        &nearPlaneLocation[2]);

GLdouble farPlaneLocation[3];
gluUnProject(winX, winY, 1, modelViewMatrix, projectionMatrix,
             viewport, &farPlaneLocation[0], &farPlaneLocation[1],
        &farPlaneLocation[2]);

 QVector3D nearP = QVector3D(nearPlaneLocation[0], nearPlaneLocation[1],
            nearPlaneLocation[2]);
 QVector3D farP = QVector3D(farPlaneLocation[0], farPlaneLocation[1],
            farPlaneLocation[2]);

我觉得我在使用冲突的系统什么的。

我应该使用不同的变量来管理我的相机吗?我看到有关投影视图、模型视图等的讨论,但我不知道如何使用它以及如何使用着色器程序。说到OpenGL,我还是新手。

澄清一下: 我正在尝试将我的鼠标坐标转换为 3D space 坐标。到目前为止,它似乎半工作,只是没有考虑相机旋转。我确认我的问题与光线创建或坐标未投影有关,而不是与我实际的光线拾取逻辑有关。

看起来你被混合不同的功能绊倒了groups/levels。您有使用固定函数矩阵堆栈的某些方面。例如,在您的绘图函数中:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();    
gluLookAt(cameraPosition.x(), cameraPosition.y(), cameraPosition.z(), camX, camY, 0, cameraUpDirection.x(), cameraUpDirection.y(), cameraUpDirection.z());

或者在你的反投影函数中:

glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);

但在其他地方,您正在构建自己的矩阵,并将它们作为制服传递到着色器中:

vMatrix.lookAt(cameraPosition, QVector3D(camX, camY, 0), cameraUpDirection);
...
shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);

这是实现相同功能的两种不同方式。但是你不能随意混合和匹配它们的各个部分。如果你想让事情保持清洁和可预测,你应该选择一个,并坚持下去。由于第一个是已弃用的遗留功能,我建议您坚持构建自己的矩阵,并使用着色器代码中定义的制服。

举例说明为什么您当前的功能组合令人失望:您使用 glGetDoublev(GL_PROJECTION_MATRIX, ...) 从固定函数矩阵堆栈获取当前投影矩阵。但至少在此处显示的代码中,您永远不会使用矩阵堆栈指定投影矩阵。所以这只是给你单位矩阵。就 OpenGL 而言,作为制服传递给着色器程序的 pMatrix 矩阵完全不相关。

重写你的整个代码对于这个答案来说有点多,所以这里只是关键部分的指针:

  • 删除所有引用矩阵堆栈的调用。这包括 glMatrixMode()glLoadIdentity()gluLookAt() 之类的调用以及获取当前矩阵的 glGetDoublev() 调用。
  • 为所有渲染使用着色器(如果您还没有使用的话),并在 GLSL 代码中将所有需要的矩阵定义为 uniform。
  • 使用您自己的代码或广泛使用的 matrix/vector 库之一自行计算和管理矩阵。
  • 将这些矩阵作为统一传递给着色器。

现在,对于非投影,您似乎已经有了模型、视图和投影矩阵(pMatrixvMatrixmMatrix)。所以不需要任何 glGet*() 调用来从 OpenGL 检索它们。 vMatrixmMatrix得到modelViewMatrix,直接用pMatrix作为传递给gluUnProject().

的投影矩阵

严格来说,我也认为 GLU 已弃用。但是,如果您仍然愿意使用 gluUnProject(),那现在可能是最简单的。否则,常用的矩阵库很可能有它的实现。或者,如果您不怕弄脏自己的双手,那么如果您查找一些解释基础计算的 specs/documentation,实施起来应该不难。

在这里,我想我遇到了一系列问题。

  1. 我应该为我的近平面使用 -1,而不是 0
  2. 我的矩阵有不同的数据类型(特别是 QT 的),因此我无法将它们直接插入到 unproject 函数中。

我通过执行以下操作解决了这些问题:

  1. 我安装了 GLM library 来标准化我的数据类型
  2. 我自己进行了矩阵计算
  • 所以我将矩阵拉入我的光线创建函数,然后将逆视图矩阵乘以逆模型矩阵,然后乘以逆投影矩阵。
  • 然后这个值将乘以屏幕坐标的 2 个不同向量(必须在 NDC space 中)。一个的 z 为 -1,另一个为 +1,对应于 window space 的近平面和远平面。最后,这些向量还必须具有 1 的“w”值,以便矩阵 * 向量的计算结果可以除以其自身的最后一个点。

我在这里提出了一系列问题,因为我最初的光线拾取问题导致我的代码中出现了一系列错误。 如果有人遇到这里发布的问题,可能值得查看我打开的其他问题,因为我所有的问题都源于投影问题:

  • ,我了解到我实际上需要某种形式的计算才能创建射线。
  • ,我了解到 unProject 不起作用,因为我试图使用 OpenGL 函数拉模型和查看矩阵,但我从来没有真正设置它们,因为我构建了矩阵用手。我分两次解决了这个问题:我手动进行了数学运算,并制作了相同数据类型的所有矩阵(它们之前是混合数据类型,也导致了问题)。
  • And lastly, in here,我了解到我的操作顺序有点不对(需要将矩阵乘以向量,而不是相反),我的近平面需要是-1,而不是0,而且将与矩阵相乘的向量的最后一个值(值“w”)需要为 1。

由于我的问题持续了相当长的时间,并提出了几个问题,我要感谢一路上帮助过我的几个人: