Qt OpenGL 鼠标拾取实现

在之前的文章中讲到了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(
00, 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.01000.0);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(
0.00.010.00.00.00.00.01.00.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(), 
55, viewport);
    
const float aspect = static_cast<float>(viewport[2]) / viewport[3];
    gluPerspective(
45.0, aspect, 1.01000.0);

    
// Draw
    paintGL();

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

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

    
// Show selection
    printf("%d hits ", hits);
    
if (hits > 0)
    {
        
int id = 0;
        
for (int i = 0; i < hits; i++)
        {
            printf(
"Level: %u ", selectBuffer[id + 0]);
            printf(
"  Min: %f ", (double)selectBuffer[id + 1] / UINT_MAX);
            printf(
"  Max: %f ", (double)selectBuffer[id + 2] / UINT_MAX);
            printf(
"   ID: %u ", 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();
}

运行测试:

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

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

仅供参考,出自github:https://github.com/tatsy/QtOpenGLMousePick

原文地址:https://www.cnblogs.com/MakeView660/p/10648707.html