Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QOpenGLWidget getting wrong pixel at mouse position
QtWS25 Last Chance

QOpenGLWidget getting wrong pixel at mouse position

Scheduled Pinned Locked Moved Solved General and Desktop
openglopenglwidgetmouse pickingmouse event
2 Posts 1 Posters 431 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Alojz Holubek
    wrote on 17 Apr 2024, 00:28 last edited by Alojz Holubek
    #1

    Good day,

    I am learning the QT framework through a project that uses a QTOpenGLWidget to visualize a 3D object, i would now like to allow the user to be able to pick any triangle face of the mesh, for now i would just like to highlight that face in a different color.

    I decided to go with 'color picking' as my method of selection, I render a special version of the model where each triangle side has a different color, then i get the position of the mouse within the widget and read the pixel color from it, thus getting a unique ID for the selected face.

    My program basically works as described, but the mouse position seems to have a weird offset and I am not sure where it is coming from, here are some examples (apologies for the color scheme, doing simple RGB for now):

    c6dc8ca6-0d06-4fcf-91f8-0c6ce6e5a860-image.png
    In this example, i clicked on the bottom right side of the cube, but the top left side is highlighted.

    8aa7ebd9-ae50-4840-9479-ebd6652849b2-image.png
    Here i am clicking on the top left part, but no face is highlighted.

    3498189f-5b76-4bbb-9405-34d213474d81-image.png
    Finally here i clicked completely outside the boundaries of the cube, but i still get a highlighted face.

    Here is the code i think is the most relevant to this issue, first the left click event handler:

    void Visualizer::mousePressEvent(QMouseEvent* event)
    {
        QPoint currentPos = event->pos();
        // left button: rotate the model
        if (event->button() == Qt::LeftButton) {
            isRotating = true;
            lastMousePos = event->pos();
    
    
            mouseX = currentPos.x();  //mouseX and mouseY are of type int
            mouseY = currentPos.y();
            update();
    
        }
        // right button: drag and move the object around
        if (event->button() == Qt::RightButton) {
            isDragging = true;
            lastMousePos = event->pos();
        }
    }
    

    Here is the paintGL function that first paints the color pick mesh before clearing the screen and painting the actual mesh, I understand that this is a 'hacky' way of implementing color picking, but since i do not plan on working with large number of faces it should suffice for my needs:

    void Visualizer::paintGL()
    {
        projectionMatrix.setToIdentity();
        viewMatrix.setToIdentity();
        modelMatrix.setToIdentity();
    
        float aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
        projectionMatrix.perspective(45, aspectRatio, 0.1f, 10000.0f);
    
        viewMatrix.translate(0, 0, translateZ);
        viewMatrix.rotate(m_rotationAngleX, 1.0f, 0.0f, 0.0f);
        viewMatrix.rotate(m_rotationAngleY, 0.0f, 1.0f, 0.0f);
        
        modelMatrix.translate(translateX, translateY, 0.0f);
        modelMatrix.scale(1.0f / zoomLevel);
    
        //combine the matrices to form mvp matrix
        mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;
    
    
        QMatrix4x4 invViewMatrix = viewMatrix.inverted();
        QVector3D cameraPosition = invViewMatrix.column(3).toVector3D();
        //qDebug() << cameraPosition;
    
    
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
    
        if (objectLoaded) {
            drawPickFrame();
            glFinish();
            glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glEnable(GL_DEPTH_TEST);
            
    
            shaderProgram.bind();
            shaderProgram.setUniformValue("mvpMatrix", mvpMatrix);
    
            
            //QVector3D cameraPosition = viewMatrix.inverted().column(3).toVector3D();
            QVector3D lightDirection = cameraPosition.normalized();
            shaderProgram.setUniformValue("lightDirection", lightDirection);
    
            glBindVertexArray(vao);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
            glFinish();
            for (int i = 0; i < mesh.indices.size(); i += 3) {
                
                QVector3D v0 = mesh.vertices[mesh.indices[i]].normalized();
                QVector3D v1 = mesh.vertices[mesh.indices[i + 1]].normalized();
                QVector3D v2 = mesh.vertices[mesh.indices[i + 2]].normalized();
    
                QVector3D edge1 = (v1 - v0).normalized();
                QVector3D edge2 = (v2 - v0).normalized();
                QVector3D faceNormal = QVector3D::crossProduct(edge1, edge2).normalized();
                float shading = qMax(0.0f, QVector3D::dotProduct(faceNormal, lightDirection ));
    
                int isSelected = 0;
                if (i / 3 == selectedFace) {
                    isSelected = 1;
    
    
    			}
    
                shaderProgram.setUniformValue("selectedFace", isSelected);
    
                //qDebug() << shading;
                shaderProgram.setUniformValue("shadingValue", shading);
                glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, reinterpret_cast<void*>(sizeof(GLuint) * i));
            }
        }
        shaderProgram.release();
        glBindVertexArray(0);
        //update();
    }
    

    Finally, here is the color picking frame function, like i said it seems to be working correctly, it's just the actual pixel location seems to be offsetted by some values...

    void Visualizer::drawPickFrame() {
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
    
        for (size_t i = 0; i < mesh.indices.size(); i += 3) {
            GLuint id = i / 3; // ID corresponding to the triangle
            int red = (id >> 16) & 0xFF;
            int green = (id >> 8) & 0xFF;
            int blue = id & 0xFF;
            //qDebug() << "ID: " << id;
            QColor color(red, green, blue);
    
            QVector3D vertex1 = mesh.vertices[mesh.indices[i]];
            QVector3D vertex2 = mesh.vertices[mesh.indices[i + 1]];
            QVector3D vertex3 = mesh.vertices[mesh.indices[i + 2]];
    
            vertex1 = mvpMatrix.map(vertex1);
            vertex2 = mvpMatrix.map(vertex2);
            vertex3 = mvpMatrix.map(vertex3);
    
    
            float r = color.redF();
            float g = color.greenF();
            float b = color.blueF();
    
    
            glBegin(GL_TRIANGLES);
            glColor3f(r, g, b);
            glVertex3f(vertex1.x(), vertex1.y(), vertex1.z());
            glVertex3f(vertex2.x(), vertex2.y(), vertex2.z());
            glVertex3f(vertex3.x(), vertex3.y(), vertex3.z());
            glEnd();
        }
        glFinish();
        unsigned char pixel[3];
        glReadPixels(mouseX, height() - mouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixel);
    
    
        //qDebug() << mouseX << mouseY;
    
        GLuint id = (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
        qDebug() << "within ID: " << id;
        selectedFace = id;
        glFinish();
    }
    

    Finally i add my initializeGL and resizeGL functions, although i don't think the problem will lie here:

    void Visualizer::initializeGL()
    {
        initializeOpenGLFunctions();
    
        glEnable(GL_DEPTH_TEST);
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // Set the clear color to blue
        
       
        // Create and bind the VAO, VBO 
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
    
        // Set up the vertex buffer data with a null buffer
        glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
    
        glGenBuffers(1, &indexBuffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        // Set up the index buffer data with a null buffer
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
        // Specify the format of the vertex data (position in this case)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr);
        glEnableVertexAttribArray(0);
    
    
    
        // Create and bind the  buffer
        glGenBuffers(1, &shadingBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, shadingBuffer);
        glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_STATIC_DRAW);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr);
        glEnableVertexAttribArray(1);
    
        glBindVertexArray(0);
    
        // Load the shader program
        shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, "vertex_shader.glsl");
        shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, "fragment_shader.glsl");
        //shaderProgram.addShaderFromSourceFile(QOpenGLShader::Geometry, "geometry_shader.glsl");
        shaderProgram.link();
    
        // Set the default matrices
        viewMatrix.setToIdentity();
        projectionMatrix.setToIdentity();
        modelMatrix.setToIdentity();
        float aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
        projectionMatrix.perspective(45.0f, aspectRatio, 0.1f, 10000.0f); // fov?
    }
    
    /*
    *   Function: inherited function that is used when the user resizes the application
    */
    void Visualizer::resizeGL(int w, int h)
    {
        glViewport(0, 0, w, h);
    
        projectionMatrix.setToIdentity();
        float aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
        projectionMatrix.perspective(45.0f, aspectRatio, 0.1f, 10000.0f);
    }
    

    Any help would be much appreciated, thank you.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      Alojz Holubek
      wrote on 18 Apr 2024, 02:38 last edited by
      #2

      Dug around other similar topics and found a solution, posting it here in case anyone has a similar problem to mine. The solution is in this thread

      It seems this is a 'problem' with high DPI displays in particular. I changed the way the program gets the X and Y coordinates from the mouse by taking into account the devicePixelRatio() as outlined in the linked thread, which solved the offsetting.

      1 Reply Last reply
      2
      • A Alojz Holubek has marked this topic as solved on 18 Apr 2024, 02:38

      1/2

      17 Apr 2024, 00:28

      • Login

      • Login or register to search.
      1 out of 2
      • First post
        1/2
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved