QOpenGLWidget坐标转换

在集成一款产品的SDK时遇到以下问题:
SDK使用了QOpenGLWidget编写,目的是绘制二维图形。现在想要在QOpenGLWidget上增加鼠标交互,需要获取鼠标点击的位置在QOpenGLWidget绘制的二维区域对应的世界坐标。
SDK的paintGL代码:

     //观察矩阵view
        QMatrix4x4 camera;
    camera.lookAt(QVector3D(0, 0, 1.3f * cameraZoom), QVector3D(0, 0, 0), QVector3D(0, 1, 0));
    camera.translate(0.0f, -0.5f, 0.0f);
    camera.scale(1.0f, 1.0f, 1.0f);

    // 投影矩阵project
        QMatrix4x4 projection;
        projection.setToIdentity();//初始化为单位矩阵
        projection.perspective(45.f, (float)width / (float)height, 0.01f, 100.0f);

    QMatrix4x4 m = projection * camera;
    paintSonarImage(m.constData(), glfLogger);/
    ...
    // bind matrix
    glUniformMatrix4fv(m_matrixUniform, 1, GL_FALSE, mvp);
    ...

SDK添加了两个矩阵,传入了shader,在这种情况下,如何获取正确的鼠标位置在二维世界中的投影

下面是我尝试的代码,但是结果并不对


    QMatrix4x4 m = projection * camera;
    paintSonarImage(m.constData(), glfLogger);
#if 1
    {
        float winZ;
        QPointF pos = line.p1();
        glReadPixels((int)pos.x(), (int)pos.y(),
            1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);

        float x = (2.f * pos.x()) / width - 1.f;
        float y = 1.f - (2.0f * pos.y()) / height;
        float z = winZ * 2.f - 1.f;

        float w = (2.f * 0.01f * 100.0f) / (100.0f + 0.01f - z * (100.0f - 0.01f));

        QVector4D worldPos(x, y, z , 1);
        qDebug() << "worldPos1 = " << worldPos;
        worldPos *= w;
        worldPos = camera.inverted() * projection.inverted() * worldPos;
        qDebug() << "pos = " << pos;
        qDebug() << "worldPos2 = " << worldPos;
    }
#endif

“Devil组”引证GPT后的撰写:
可以通过以下步骤实现:

  • 在QOpenGLWidget的mousePressEvent或者mouseReleaseEvent中获取鼠标点击位置的屏幕坐标(x, y)。
  • 使用glReadPixels函数获取鼠标点击位置的深度值winZ。
  • glReadPixels((int)x, (int)y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);

注意:glReadPixels函数获取的深度值范围为[0,1],需要将其转换为[-1,1]的范围。

  • 将屏幕坐标(x, y)转换为标准化设备坐标(NDC)的范围[-1,1]内的坐标(x_ndc, y_ndc)。

float x_ndc = (2.0f * x) / width - 1.0f;
float y_ndc = 1.0f - (2.0f * y) / height;

  • 其中,width和height为QOpenGLWidget的宽度和高度。
  • 将屏幕坐标(x, y)和深度值winZ转换为裁剪空间坐标(x_clip, y_clip, z_clip, w_clip)。

QVector4D clipPos(x_ndc, y_ndc, winZ, 1.0f);

  • 将裁剪空间坐标转换为视口坐标(x_viewport, y_viewport, z_viewport, w_viewport)。

QVector4D viewportPos = mvpMatrix.inverted() * clipPos;
float w_viewport = viewportPos.w();
float x_viewport = viewportPos.x() / w_viewport;
float y_viewport = viewportPos.y() / w_viewport;
float z_viewport = viewportPos.z() / w_viewport;

  • 将视口坐标转换为世界坐标。

QVector3D worldPos = cameraMatrix.inverted() * QVector3D(x_viewport, y_viewport, z_viewport);

这个是提问的我,还是我被推荐回答了。你好提问者,不知道你有没有使用过glut库,这个是opengl的第三方库,上面的转换代码是一个基础射线转换代码,如果是二维的话你可以用glut的函数来获取。代码如下:

double modelview[16], projection[16];//模型投影矩阵
  int viewport[4];//视口
  float ScreenZ = 1;//深度值
  double objx,objy,objz;//获得的世界坐标值
  glGetDoublev( GL_PROJECTION_MATRIX, projection );//获得投影矩阵
  glGetDoublev( GL_MODELVIEW_MATRIX, modelview );//获得模型矩阵
  glGetIntegerv( GL_VIEWPORT, viewport );    //获得视口
  glReadPixels( x, viewport[3]-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &ScreenZ ); //获得屏幕像素对应的世界坐标深度值  
 gluUnProject( x, viewport[3]-y, ScreenZ , modelview, projection, viewport, &objx, &objy, &objz );//获得屏幕坐标对应的世界坐标
std::cout << " x:" << objx << " y:" << objy << " z:" << objz << std::endl;

你可以试一试,对了这个里面的参数可能和你的有冲突,别忘了修改.
如果你需要库文件的话可以找我或者是在网上下载。这个里面还有好多的计算,自己研究哈