Color picking with glReadPixels works with offset on Android

98 Views Asked by At
  • Qt 6.6.1
  • Qt 6.7.0 beta
  • Windows 10

I wrote a simple example that shows a problem. This example draws a red triangle on the background with (0.2, 0.2, 0.2) color. I implemented the mousePressEvent method. When you click on the canvas the example will print a color to the console. I tested this example on Windows 10 and WebAssembly - it works as expected. For example how it works on WebAssembly:

enter image description here

But on Android (both: real device connected with USB-cable and Android Emulator) it works with offset. So I draw a triangle in the center but it looks like I draw it in the top right corner. When I touch on the triangle it prints (0.2, 0.2, 0.2) to the console but when I touch to the top right corner it prints (1, 0, 0) like the triangle in the top right corner. It looks like the beginning of coordinate system for glReadPixels is in the center of the window:

enter image description here

    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);
        // qDebug() << glGetError() << "\n";

        if (m_mouseClicked)
        {
            // Read the pixel
            GLubyte pixel[4];
            glReadPixels(m_mouseX, m_mouseY, 1, 1,
                         GL_RGBA, GL_UNSIGNED_BYTE, pixel);
            // qDebug() << glGetError() << "\n";
            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();
    }

I don't know it is bug of Qt or something else. I think glReadPixels is a very popular method to pick objects using the "Color ID" method and a lot of people had the same problem. Maybe someone know how to avoid this problem? I created a bug report where you can download a zip and try my example: https://bugreports.qt.io/browse/QTBUG-120648

Cross-refs:

1

There are 1 best solutions below

0
8Observer8 On BEST ANSWER

I found a solution in comments here: OpenGL support broken with high-dpi (Retina) on OS X. It is for macOS but it is true for Android too. The comment of Laszlo Agocs helped me:

You need to adjust the GL positions based on the devicePixelRatio(). If the window is size N,M and devicePixelRatio() is 2 then the GL framebuffer, viewport will all have a size of 2N,2M. Try multiplying mouseX and mouseY with devicePixelRatio().

Solution:

    void mousePressEvent(QMouseEvent *event) override
    {
        m_mouseX = event->pos().x() * devicePixelRatio();
        m_mouseY = (height() - event->pos().y() - 1) * devicePixelRatio();
        m_mouseClicked = true;
        update();
    }

enter image description here

main.cpp

#include <QtGui/QMouseEvent>
#include <QtGui/QOpenGLFunctions>
#include <QtOpenGL/QOpenGLBuffer>
#include <QtOpenGL/QOpenGLShaderProgram>
#include <QtOpenGL/QOpenGLWindow>
#include <QtWidgets/QApplication>

class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions
{
    int m_mouseX;
    int m_mouseY;
    bool m_mouseClicked = false;
    QOpenGLBuffer m_vertPosBuffer;
    QOpenGLShaderProgram m_program;

    void initializeGL() override
    {
        initializeOpenGLFunctions();
        glClearColor(0.2f, 0.2f, 0.2f, 1.f);
        qDebug() << "Device pixel ratio:" << devicePixelRatio();

        QString vertexShaderSource =
            "attribute vec2 aPosition;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(aPosition, 0.0, 1.0);\n"
            "}\n";

        QString fragmentShaderSource =
            "#ifdef GL_ES\n"
            "precision mediump float;\n"
            "#endif\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "}\n";

        m_program.create();
        m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Vertex,
                                          vertexShaderSource);
        m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Fragment,
                                          fragmentShaderSource);
        m_program.link();
        m_program.bind();

        float vertPositions[] = {
            -0.5f, -0.5f,
            0.5f, -0.5f,
            0.f, 0.5f
        };
        m_vertPosBuffer.create();
        m_vertPosBuffer.bind();
        m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions));

        m_program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 2);
        m_program.enableAttributeArray("aPosition");
    }

    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        if (m_mouseClicked)
        {
            // Read the pixel
            GLubyte pixel[4];
            glReadPixels(m_mouseX, m_mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
            // qDebug() << glGetError() << "\n";
            qDebug() << pixel[0] / 255.f << pixel[1] / 255.f << pixel[2] / 255.f;
            m_mouseClicked = false;
        }
    }

    void mousePressEvent(QMouseEvent *event) override
    {
        m_mouseX = event->pos().x() * devicePixelRatio();
        m_mouseY = (height() - event->pos().y() - 1) * devicePixelRatio();
        m_mouseClicked = true;
        update();
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    OpenGLWindow w;
    w.show();
    return app.exec();
}

pick-color-of-simple-triangle-qopenglwindow-qt6-cpp.pro

QT += core gui opengl widgets

win32: LIBS += -lopengl32

CONFIG += c++17

SOURCES += \
    main.cpp

TARGET = app