使用glReadPixels取色在Android平台存在偏移问题
Qt 6.7 Android平台glReadPixels坐标系偏移问题修复
问题场景
使用Qt 6.7在Windows 10开发OpenGL示例程序:背景色设为(0.2, 0.2, 0.2),绘制红色三角形;实现mousePressEvent方法,点击画布时通过glReadPixels获取点击位置颜色并打印。该程序在Windows 10和WebAssembly环境运行正常,但在Android真机(USB连接)及模拟器中出现坐标偏移:点击实际三角形区域输出背景色(0.2, 0.2, 0.2),点击右上角区域却输出红色(1, 0, 0),疑似glReadPixels的坐标系与窗口显示坐标系不匹配。
问题代码
void paintGL() override { glClearColor(0.2f, 0.2f, 0.2f, 1.f); glClear(GL_COLOR_BUFFER_BIT); m_program.bind(); m_vertPosBuffer.bind(); m_program.setAttributeBuffer(m_aPositionLocation, GL_FLOAT, 0, 2); m_program.enableAttributeArray(m_aPositionLocation); glDrawArrays(GL_TRIANGLES, 0, 3); if (m_mouseClicked) { GLubyte pixel[4]; glReadPixels(m_mouseX, m_mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); qDebug() << pixel[0] / 255.f << pixel[1] / 255.f << pixel[2] / 255.f << "\n"; qDebug() << m_mouseX << m_mouseY << "\n"; m_mouseClicked = false; } } void mousePressEvent(QMouseEvent *event) override { m_mouseX = event->pos().x(); m_mouseY = height() - event->pos().y() - 1; m_mouseClicked = true; update(); }
问题原因
Android平台的OpenGL ES上下文存在帧缓冲区尺寸与窗口显示尺寸不匹配的情况:高DPI屏幕会触发系统缩放,Qt的OpenGLWidget窗口尺寸(width()/height())是逻辑尺寸,而实际渲染的帧缓冲区尺寸是物理像素尺寸。直接使用窗口尺寸转换鼠标坐标,会导致glReadPixels读取的位置与屏幕显示位置错位。
修复方案
通过获取OpenGL真实帧缓冲区尺寸来转换鼠标坐标,确保点击位置与读取位置完全对应:
- 在类中添加成员变量存储帧缓冲区尺寸:
QSize m_framebufferSize;
- 重写
resizeGL方法,实时更新帧缓冲区尺寸并设置视口:
void resizeGL(int w, int h) override { m_framebufferSize = framebufferSize(); glViewport(0, 0, m_framebufferSize.width(), m_framebufferSize.height()); }
- 修改
mousePressEvent中的坐标转换逻辑:
void mousePressEvent(QMouseEvent *event) override { // 将鼠标位置转换为窗口归一化坐标 float normX = static_cast<float>(event->pos().x()) / width(); float normY = static_cast<float>(event->pos().y()) / height(); // 转换为帧缓冲区物理像素坐标,适配glReadPixels的左下角原点 m_mouseX = static_cast<int>(normX * m_framebufferSize.width()); m_mouseY = m_framebufferSize.height() - static_cast<int>(normY * m_framebufferSize.height()) - 1; m_mouseClicked = true; update(); }
修复说明
framebufferSize()返回OpenGL实际渲染的物理像素尺寸,自动处理Android高DPI缩放问题- 通过归一化坐标转换,保证逻辑点击位置与物理帧缓冲区位置一一对应
glReadPixels的Y轴原点在左下角,因此需要用帧缓冲区高度减去转换后的Y值完成坐标系翻转
内容的提问来源于stack exchange,提问作者8Observer8
相关产品推荐
相关产品推荐

