QOpenGLWidget 中求解屏幕坐标到世界坐标的变换不正确。
顶点着色器代码如下:
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
initializeGL()代码如下:
void MyGLWidget::initializeGL()
{
initializeOpenGLFunctions(); // 初始化OpenGL函数
mShader = new QOpenGLShaderProgram();
if (!mShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex.vert")) { //添加并编译顶点着色器
qDebug() << "ERROR:" << mShader->log(); //如果编译出错,打印报错信息
}
if (!mShader->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment.frag")) { //添加并编译片段着色器
qDebug() << "ERROR:" << mShader->log(); //如果编译出错,打印报错信息
}
if (!mShader->link()) { //链接着色器
qDebug() << "ERROR:" << mShader->log(); //如果链接出错,打印报错信息
}
mVao = new QOpenGLVertexArrayObject();
mVbo = new QOpenGLBuffer(QOpenGLBuffer::Type::VertexBuffer);
mVao->create();
mVao->bind();
mVbo->create();
mVbo->bind();
// 将顶点数据分配到VBO中 pra1-顶点数据 pra2-字节长度
mVbo->allocate(vertices.data(), sizeof(float)*vertices.size());
mShader->setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 3);
mShader->enableAttributeArray(0);
mVbo->release();
mVao->release();
}
paintGL()代码如下:
glClearColor(0.0f, 0.2f, 0.0f, 1.0f); // 清除颜色信息
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除缓存/深度数据
mShader->bind();
// 模型矩阵 局部空间->世界空间
QMatrix4x4 model;
model.rotate(0, QVector3D(1.0f, 0.0f, 0.0f));
mShader->setUniformValue("model", model);
qDebug() << "Model = " << model;
// 观察矩阵 世界空间->观察空间
QMatrix4x4 view;
view.translate(cameraPos);
//view.lookAt(cameraPos, cameraTarget, cameraUp);
mShader->setUniformValue("view", view);
qDebug() << "view = " << view;
// 投影矩阵 观察空间->裁剪空间 重要作用:将(x,y)->(-1,1)
QMatrix4x4 projection;
projection.ortho(clipXArea.first, clipXArea.second, clipYArea.first, clipYArea.second, 1, -1);
mShader->setUniformValue("projection", projection);
qDebug() << "Projection = " << projection;
glViewport(0, 0, width(), height()); // 本应该放在resizeGL()中,但是在Qt中测试,在resizeGL中无效,故写在这里
mVao->bind();
glPointSize(10.0f);
glDrawArrays(GL_POINTS, 0, 3); // 画点云 pra1-图元类型 pra2-从数组的哪个位置开始绘制 pra3-绘制数量
mShader->release();
mVao->release();
}
根据屏幕坐标获取世界坐标的实现函数如下:
QPoint MyGLWidget::getOriginPos(const QPoint & pos)
{
#pragma region 自己逆乘
// 原理 屏幕坐标 = port * projection * view * model * 原始坐标 (注意是左乘)
// 模型矩阵 局部空间->世界空间
QMatrix4x4 model;
model.rotate(0, QVector3D(1.0f, 0.0f, 0.0f));
// 观察矩阵 世界空间->观察空间
QMatrix4x4 view;
//view.translate(cameraPos);
view.lookAt(cameraPos, cameraTarget, cameraUp);
mShader->setUniformValue("view", view);
// 投影矩阵 观察空间->投影空间 作用:将(x,y)->(-1,1)
QMatrix4x4 projection;
projection.ortho(clipXArea.first, clipXArea.second, clipYArea.first, clipYArea.second, 0, 0);
// 视口矩阵 投影空间->屏幕空间
QMatrix4x4 port;
port.viewport(0, 0, width(), height());
qDebug() << "port = " << port;
qDebug() << "width = " << width() << "height = " << height();
QVector4D posInMat = QVector4D(pos.x(), pos.y(), 0, 1);
QVector4D retPosInMat = model.inverted() * view.inverted() * projection.inverted() * port.inverted() * posInMat;
#pragma endregion
#pragma region 调用glu的glUnproject
GLdouble modelView[16], projectMat[16]; // 模型矩阵、投影矩阵
GLint viewPort[4]; // 视口
double objx, objy, objz; // 获得的世界坐标
glGetDoublev(GL_PROJECTION_MATRIX, projectMat);
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetIntegerv(GL_VIEWPORT, viewPort); // 视口
gluUnProject(pos.x(), pos.y(), 0, modelView, projectMat, viewPort, &objx, &objy, &objz);
retPosInMat.setX(objx);
retPosInMat.setY(objy);
#pragma endregion
return QPoint(retPosInMat.x(), retPosInMat.y());
}
void MyGLWidget::mousePressEvent(QMouseEvent * event)
{
QPoint pos = event->pos();
QPoint retPos = getOriginPos(pos);
qDebug() << "pos in disply : " << pos << " its origin pos : " << retPos;
}
测试用的顶点数据如下:
vertices = {
-400.0f, 400.0f, 0.0f,
400.0f, 400.0f, 0.0f,
400.0f, -400.0f, 0.0f
};
附测试过程截屏:
求解结果一直是错误的,自己暂时找不出原因在哪。寻求大家帮助解惑,感谢感谢~
看了你的代码,请检查以下几部分:
从你的代码来看 感觉问题出在获取世界坐标时的矩阵运算顺序上
你的getOriginPos函数中,矩阵乘法顺序是反的:
model.inverted() * view.inverted() * projection.inverted()
更正矩阵运算顺序:
世界坐标 = 逆投影矩阵 x 逆视图矩阵 x 逆模型矩阵 x 屏幕坐标
【相关推荐】
根据你提供的代码和描述,可能出现问题的地方包括以下几个方面:
矩阵运算的顺序和方向: 在计算变换矩阵时,矩阵的乘法顺序和乘法的方向非常重要。在 OpenGL 中,变换矩阵是从右往左乘的,也就是说,变换是从局部空间到世界空间,再到观察空间,最后到裁剪空间。你在获取世界坐标的函数 getOriginPos
中逆乘的顺序应该是:port.inverted() * projection.inverted() * view.inverted() * model.inverted()
。
正交投影矩阵设置: 在投影矩阵的 ortho
方法中,你的 zNear
和 zFar
参数都设置为 0,这可能会导致投影矩阵设置有问题。zNear
和 zFar
参数应该设置为一个合理的范围,比如 -1
和 1
。
屏幕坐标和窗口大小: 在获取屏幕坐标后,应该根据窗口的实际大小进行调整,以得到正确的世界坐标。
投影矩阵设置: 确保你的投影矩阵设置正确,将场景映射到裁剪空间。
顶点坐标的范围: 确保你的顶点坐标范围在合理的范围内,不要超过裁剪空间的范围。
请检查以上几个方面,逐步排除问题。如果问题仍然存在,你可以通过打印输出或调试工具来查看矩阵运算的结果,以便更好地定位问题所在。同时,确保你的顶点数据在屏幕范围内,且在正确的坐标空间内。