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
Forum Updated to NodeBB v4.3 + New Features

QOpenGLWidget getting wrong pixel at mouse position

Scheduled Pinned Locked Moved Solved General and Desktop
openglopenglwidgetmouse pickingmouse event
2 Posts 1 Posters 459 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