我已经尝试将鼠标坐标转换为OpenGL场景中的3D空间坐标有一段时间了。
目前,我的投影有点乱(我认为),当我在场景中移动时,它似乎没有完全考虑到我的"相机"。为了检查这一点,我画了一条线。
我的调整大小功能:
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();
创建我的Ray:
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的新手。
所以要澄清:我正在尝试将鼠标坐标转换为三维空间坐标。到目前为止,它似乎是半工作的,只是没有考虑到相机的旋转。我确认我的问题与光线创建或坐标的取消投影有关,而不是与我的实际光线拾取逻辑有关。
它看起来很像是被混合不同的功能组/级别绊倒了。您已经了解了使用固定函数矩阵堆栈的一些方面。例如,在绘图功能中:
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代码中将所需的所有矩阵定义为统一矩阵
- 您可以使用自己的代码或广泛使用的矩阵/向量库来计算和管理矩阵
- 将这些矩阵作为一致性传递给着色器
现在,对于非投影,看起来您已经有了模型、视图和投影矩阵(pMatrix
、vMatrix
、mMatrix
)。因此,不需要任何glGet*()
调用来从OpenGL中检索它们。将vMatrix
与mMatrix
相乘得到modelViewMatrix
,并直接使用pMatrix
作为传递给gluUnProject()
的投影矩阵。
严格地说,我也认为GLU是被否决的。但是,如果您仍然可以放心地使用gluUnProject()
,那么这可能是目前最简单的。否则,常用的矩阵库可能会有它的实现。或者,如果你不怕弄脏你的手,如果你查找一些解释底层计算的规范/文档,它应该不会很难实现。
在这里,我认为我遇到了一系列问题。
- 我应该用-1表示我的近距离飞机,而不是0
- 我的矩阵有不同的数据类型(特别是QT),这使我无法直接将它们插入到unproject函数中
我通过以下操作解决了这些问题:
- 我安装了GLM库来规范我的数据类型
- 我自己进行矩阵计算
- 所以我把矩阵拉到我的光线创建函数中,然后把逆视图矩阵乘以逆模型矩阵,然后乘以逆投影矩阵
- 然后将该值乘以屏幕坐标的2个不同矢量(必须在NDC空间中)。一个z为-1,另一个为+1,对应于窗口空间的近平面和远平面。最后,这些矢量还必须具有"0";w";值为1,因此矩阵*向量的计算结果可以除以其自身的最后一个点
我在这里打开了一系列问题,因为我最初的光线拾取问题导致我的代码中出现了一系列错误。如果有人遇到了这里发布的问题,可能值得查看我打开的其他问题,因为我所有的问题都源于投影问题:
- 在这里,我了解到,为了创建光线,我实际上需要某种形式的计算
- 在这里,我了解到unProject不起作用,因为我试图使用OpenGL函数提取模型和视图矩阵,但我从来没有真正设置过它们,因为我是手工构建矩阵的。我分两步解决了这个问题:我手动计算,并制作了相同数据类型的所有矩阵(它们之前是混合数据类型,也会导致问题)
- 最后,在这里,我了解到我的运算顺序有点偏离(需要将矩阵乘以一个向量,而不是相反),我的近平面需要是-1,而不是0,并且将与矩阵相乘的向量的最后一个值(值"w")需要是1
由于我的问题持续了很长一段时间,并回答了几个问题,我要感谢一路上帮助我的几个人:
- 在这个线程中,面冲头的srobins
- 在这个问题和这个讨论中