在QT中使用QOpenGLWidget时,鼠标点击读取像素深度glReadPixels遇到的问题

本文详细探讨了在使用OpenGL时遇到的两个主要问题:一是如何确保在鼠标点击事件中获取到正确的像素深度值,需要通过调用`makeCurrent()`来更新当前内容;二是当启用了自适应高分辨率后,由于缩放导致的坐标转换问题,需要通过调整坐标并考虑设备像素比来获取准确的深度值。解决方案包括在`mousePressEvent`中调用`makeCurrent()`和使用缩放比例转换屏幕坐标。
摘要由CSDN通过智能技术生成

需求和问题

通过鼠标点击获取某一像素的深度值z, 但获取的z始终为1

原因一,所见内容并非实时

参考这篇回答,可以看到必须要手动调用makecurrent()使当前内容active(想了几个词都不知道怎么翻译比较好),在qt文档中也可以看到:

void QOpenGLWidget::makeCurrent()

Prepares for rendering OpenGL content for this widget by making the corresponding context current and binding the framebuffer object in that context.
It is not necessary to call this function in most cases, because it is called automatically before invoking paintGL().

故在mousePressEvent(QMouseEvent* event)函数内必须手动调用才可以确保当前opengl内容所得即所见。

原因二,开启自适应高分辨率后的缩放问题

开启自适应高分辨率QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);后,qopenglwidget会进行缩放,但其内部的opengl并不会随qt进行缩放,此时鼠标点击所获得的屏幕坐标需要根据缩放比例进行处理转化为opengl坐标,才能获得准确的像素深度值,代码如下。

mousePressEvent中:

void MyWidget::mousePressEvent(QMouseEvent* event) {
	makeCurrent();
	QPoint screenp= event->pos();
	float zpos = GetDepth(screenp);
	....
	....
	doneCurrent();
}

获取深度值GetDepth

// screenp: 通过mousePressEvent获得的当前鼠标点击的屏幕点
float MyWidget::GetDepth(QPoint screenp) {
	//MyWidget宽、高
	double w = this->width();
	double h = this->height();
	// 待读取的深度值
	float zpos = 0.0;
	// qopenglwidget内部的opengl不随qt缩放,读取像素需手动从qt屏幕坐标转成opengl坐标
	// opengl中y方向0点和qt屏幕中y方向0点不同
	glReadPixels(screenp.x() * device_ratio, (h - screenp.y()) * device_ratio, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zpos);
	zpos = zpos * 2 - 1.0;
	return zpos;
}

其中device_ratio在main函数中通过

device_ratio = QApplication::desktop()->devicePixelRatio();

获得

Qt OpenGL,可以通过以下步骤实现鼠标点击模型: 1. 重写QOpenGLWidget的mousePressEvent()方法,在该方法获取鼠标点击位置的屏幕坐标。 2. 将屏幕坐标转换为OpenGL坐标系的坐标。 3. 遍历模型的所有顶点,计算每个顶点在屏幕上的坐标。 4. 判断鼠标点击位置是否在某个顶点的附近,如果是,则选该模型。 以下是一个简单的示例代码: ```c++ void MyOpenGLWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { QPoint pos = event->pos(); // 获取鼠标点击位置的屏幕坐标 // 将屏幕坐标转换为OpenGL坐标系的坐标 GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; GLfloat winX, winY, winZ; GLdouble posX, posY, posZ; glGetIntegerv(GL_VIEWPORT, viewport); glGetDoublev(GL_MODELVIEW_MATRIX, modelview); glGetDoublev(GL_PROJECTION_MATRIX, projection); winX = (float)pos.x(); winY = (float)viewport[3] - (float)pos.y(); glReadPixels(pos.x(), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ); gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); // 遍历模型的所有顶点,计算每个顶点在屏幕上的坐标 for (int i = 0; i < vertices.size(); i++) { GLfloat x = vertices[i].x; GLfloat y = vertices[i].y; GLfloat z = vertices[i].z; GLdouble winX, winY, winZ; GLint view[4]; glGetIntegerv(GL_VIEWPORT, view); gluProject(x, y, z, modelview, projection, view, &winX, &winY, &winZ); // 判断鼠标点击位置是否在某个顶点的附近,如果是,则选该模型 if (abs(winX - pos.x()) < 5 && abs(viewport[3] - winY - pos.y()) < 5) { selectedVertex = i; break; } } } } ``` 其,vertices是存储模型顶点的数组,selectedVertex是选的顶点的索引。在绘制模型,可以根据selectedVertex来高亮显示选的顶点。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值