在之前的文章中讲到了OpenGL鼠标拾取操作的例子,工作中需要在Qt中实现,下面的程序演示了QT中opengl的拾取例子。

本例子在Qt5.12和Qt Creator4.8.0上测试,使用的是QOpenGLWidget类,在窗口的正中央有红绿两个三角形组成一个正方形,分别点击不同的三角形部分进行对象拾取。

相关代码如下:



opengl_widget.h



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


 


#ifdef _MSC_VER
#pragma once
#endif

#ifndef _OPENGL_WIDGET_H_
#define _OPENGL_WIDGET_H_

#include <vector>

#include <QtWidgets/qopenglwidget.h>

class OpenGLWidget : public QOpenGLWidget
{
    Q_OBJECT

public:
    explicit OpenGLWidget(QWidget *parent = nullptr);
    virtual ~OpenGLWidget();

protected:
    virtual void initializeGL() override;
    virtual void paintGL() override;
    virtual void resizeGL(int w, int h) override;
    virtual void mousePressEvent(QMouseEvent *ev) override;
    virtual void mouseMoveEvent(QMouseEvent *ev) override;
    virtual void mouseReleaseEvent(QMouseEvent *ev) override;

private:
    void drawObjects() const;

    using uint = unsigned int;
    static const int selectBufferSize = 100;
    std::vector<uint> selectBuffer = std::vector<uint>(selectBufferSize);


};  // class OpenGLWidget

#endif  // _OPENGL_WIDGET_H_



 

 



opengl_widget.cpp



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127


 


#include "opengl_widget.h"

#include <gl/GLU.h>

#include <QtGui/qevent.h>

OpenGLWidget::OpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
}

OpenGLWidget::~OpenGLWidget()
{
}

void OpenGLWidget::initializeGL()
{
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

void OpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    int renderMode;
    glGetIntegerv(GL_RENDER_MODE, &renderMode);

    if (renderMode != GL_SELECT)
    {
        // Matrix setting
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        const float aspect = static_cast<float>(width()) / height();
        gluPerspective(45.0, aspect, 1.0, 1000.0);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    drawObjects();
}

void OpenGLWidget::mousePressEvent(QMouseEvent *ev)
{
    // Selection buffer
    std::fill(selectBuffer.begin(), selectBuffer.end(), 0);
    glSelectBuffer(selectBufferSize, &selectBuffer[0]);

    // Draw for selection buffer
    glRenderMode(GL_SELECT);

    // Matrix setting
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    int viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    gluPickMatrix(ev->x(), height() - ev->y(), 5, 5, viewport);
    const float aspect = static_cast<float>(viewport[2]) / viewport[3];
    gluPerspective(45.0, aspect, 1.0, 1000.0);

    // Draw
    paintGL();

    // Reset matrix setting
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    // Revert render mode
    int hits = glRenderMode(GL_RENDER);

    // Show selection
    printf("%d hits\n", hits);
    if (hits > 0)
    {
        int id = 0;
        for (int i = 0; i < hits; i++)
        {
            printf("Level: %u\n", selectBuffer[id + 0]);
            printf("  Min: %f\n", (double)selectBuffer[id + 1] / UINT_MAX);
            printf("  Max: %f\n", (double)selectBuffer[id + 2] / UINT_MAX);
            printf("   ID: %u\n", selectBuffer[id + 3]);
            id += 4;
        }
    }
}

void OpenGLWidget::mouseMoveEvent(QMouseEvent *ev)
{
}

void OpenGLWidget::mouseReleaseEvent(QMouseEvent *ev)
{
}

void OpenGLWidget::drawObjects() const
{
    // Prepare for selection
    glInitNames();
    glPushName(0);

    // First
    glColor3f(1.0f, 0.0f, 0.0f);
    glLoadName(1);
    glBegin(GL_TRIANGLES);
    glVertex3f(-1.0f, -1.0f, 0.0f);
    glVertex3f(-1.0f,  1.0f, 0.0f);
    glVertex3f( 1.0f,  1.0f, 0.0f);
    glEnd();

    // Second
    glColor3f(0.0f, 1.0f, 0.0f);
    glLoadName(2);
    glBegin(GL_TRIANGLES);
    glVertex3f(-1.0f, -1.0f, 0.0f);
    glVertex3f( 1.0f,  1.0f, 0.0f);
    glVertex3f( 1.0f, -1.0f, 0.0f);
    glEnd();
}



运行测试:

Qt OpenGL 鼠标拾取实现_#pragma

程序中使用了printf作为输出,但是其不能直接打印在Qt的应用程序输出窗口,需要进程结束后才会打印,可以使用qDebug来替代printf。

在测试过程中,我遇到了无论点击屏幕哪里,都会将所有的对象选中的情况,查找一些资料对比试验后,提醒要注意gluLookAt、gluPerspective的调用位置。

仅供参考