
glwidget.h
cpp
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <QMouseEvent>
#include <QVector3D>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit GLWidget(QWidget *parent = nullptr);
~GLWidget() override;
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
// 鼠标事件处理
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private:
void initShaders();
QVector3D getArcballVector(int x, int y);
QOpenGLShaderProgram shaderProgram;
QMatrix4x4 projection;
QMatrix4x4 view;
QMatrix4x4 model;
// 旋转控制变量
QPoint lastMousePos;
QQuaternion rotation;
QQuaternion lastRotation;
float zoomFactor = 1.0f;
float pointSize = 550.0f; // 控制点的大小
};
#endif // GLWIDGET_H
glwidget.cpp
cpp
#include "glwidget.h"
#include <QMouseEvent>
#include <QWheelEvent>
#include <QtMath>
#include <QTimer>
GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
GLWidget::~GLWidget()
{
makeCurrent();
doneCurrent();
}
void GLWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glEnable(GL_DEPTH_TEST);
initShaders();
}
void GLWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
projection.setToIdentity();
projection.perspective(45.0f, (float)w/(float)h, 0.1f, 100.0f);
view.setToIdentity();
view.translate(0.0f, 0.0f, -5.0f);
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
model.setToIdentity();
model.scale(zoomFactor);
model.rotate(rotation);
QMatrix4x4 mvp = projection * view * model;
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", mvp);
GLfloat vertices[] = {
-0.2f, 0.3f, 0.5f, // p1
1.0f, 1.0f, 1.0f // p2
};
GLfloat colors[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f
};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_LINES, 0, 2);
// 绘制绿色原点(0,0,0)
GLfloat pointVertex[] = {0.0f, 0.0f, 0.0f};
GLfloat pointColor[] = {0.0f, 1.0f, 0.0f};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, pointVertex);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, pointColor);
glDrawArrays(GL_POINTS, 0, 1);
GLfloat vertices1[] = {
-0.2f, 0.3f, 0.5f, // p1
-1.0f, -1.0f, -1.0f // p2
};
GLfloat colors1[] = {
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, colors1);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_LINES, 0, 2);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
shaderProgram.release();
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastMousePos = event->pos();
lastRotation = rotation;
}
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
// 获取当前和之前的鼠标位置对应的球面坐标
QVector3D va = getArcballVector(lastMousePos.x(), lastMousePos.y());
QVector3D vb = getArcballVector(event->x(), event->y());
// 计算旋转轴和角度
float angle = qAcos(qMin(1.0f, QVector3D::dotProduct(va, vb))) * 180.0f / M_PI;
QVector3D axis = QVector3D::crossProduct(va, vb).normalized();
// 更新旋转
rotation = QQuaternion::fromAxisAndAngle(axis, angle) * lastRotation;
update();
}
}
void GLWidget::wheelEvent(QWheelEvent *event)
{
float delta = event->angleDelta().y() / 120.0f;
zoomFactor *= (1.0f + delta * 0.1f);
zoomFactor = qBound(0.1f, zoomFactor, 2.0f);
update();
}
QVector3D GLWidget::getArcballVector(int x, int y)
{
// 将鼠标坐标映射到[-1, 1]范围
float mx = (float)x / width() * 2.0f - 1.0f;
float my = 1.0f - (float)y / height() * 2.0f;
float length = mx * mx + my * my;
if (length <= 1.0f) {
return QVector3D(mx, my, qSqrt(1.0f - length));
} else {
// 如果超出单位球,归一化到球面
return QVector3D(mx, my, 0.0f).normalized();
}
}
void GLWidget::initShaders()
{
const char *vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec3 position;\n"
"layout(location = 1) in vec3 color;\n"
"uniform mat4 mvpMatrix;\n"
"out vec3 fragColor;\n"
"void main() {\n"
" gl_Position = mvpMatrix * vec4(position, 1.0);\n"
" fragColor = color;\n"
"}\n";
const char *fragmentShaderSource =
"#version 330 core\n"
"in vec3 fragColor;\n"
"out vec4 outColor;\n"
"void main() {\n"
" outColor = vec4(fragColor, 1.0);\n"
"}\n";
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
shaderProgram.link();
}
在mainwindow.ui中,添加OpenGLWidget,并提升为:GLWidget