OpenGL:(1)QT绘制一个可拖动和旋转的三角形

程序里的三角形可以根据鼠标移动,滚轮实现旋转。同时添加了一个定时器实现自动旋转。

代码结构: 

 代码:

QT       += core gui opengl widgets
greaterThan(QT_MAJOR_VERSION, 5)
TARGET = openGL_1
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
        main.cpp \
        mainwindow.cpp
HEADERS += \
        mainwindow.h
FORMS += \
        mainwindow.ui
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMouseEvent>
#include <QWheelEvent>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    GLWidget(QWidget *parent = nullptr);
    ~GLWidget();
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;
    void mouseReleaseEvent(QMouseEvent *event)override;
private:
    float rotationAngle;//旋转角度
    QPointF trianglePos;//三角形位置(OpenGL坐标)
    QPointF lastMousePos;//上次鼠标位置(屏幕坐标)
    bool isDragging;//是否正在拖动
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QMouseEvent>
#include <QWheelEvent>
#include <cmath>
#include <QTimer>

GLWidget::GLWidget(QWidget *parent) :
    QOpenGLWidget(parent),
    rotationAngle(0),
    trianglePos(0, 0),
    isDragging(false)
{
    setMouseTracking(true);//设置目标追踪
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this]() {
        rotationAngle += 1.0f;
        if(rotationAngle >= 360.0f) rotationAngle -= 360.0f;
        update();
    });
    timer->start(16);
    /* 大约60帧每秒
     * 参数表示定时器每隔16毫秒触发一次timeout信号
     * 1000毫秒(1秒) ÷ 16毫秒/帧 ≈ 62.5帧/秒
     */
}

GLWidget::~GLWidget()
{
    //将当前OpenGL上下文(Context)绑定到该窗口(GLWidget)。
    //OpenGL操作必须在正确的上下文中执行。析构时需要确保当前线程的上下文是该窗口的上下文,否则无法安全释放资源。
    makeCurrent();
    //解除当前上下文与窗口的绑定。 在对象销毁时,释放上下文关联,避免资源泄漏。
    doneCurrent();
}

void GLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    /* OpenGL中用于设置清除颜色缓冲区的颜色值的函数,
     * 这个函数指定了当调用glClear(GL_COLOR_BUFFER_BIT)时,屏幕将被清除成的颜色。
     * 具体参数:
     * 第一个参数 (0.2f):红色分量 (Red)--取值范围:0.0(无红色)到1.0(最大红色)
     * 第二个参数 (0.3f):绿色分量 (Green)
     * 第三个参数 (0.3f):蓝色分量 (Blue)
     * 第四个参数 (1.0f):Alpha通道(透明度)1.0表示完全不透明,0.0表示完全透明
     * RGB(51, 76.5, 76.5)  // 0.2*255≈51, 0.3*255≈76.5
     * 这个颜色看起来像是:略带红色的深蓝绿色(类似深青色/墨绿色)
     * 在OpenGL中,这个设置是"状态设置"(state-setting)函数,它不会立即改变任何东西,只是告诉OpenGL:"下次清除颜色缓冲区时,请使用这个颜色"。
     * 实际清除操作是在调用glClear(GL_COLOR_BUFFER_BIT)时执行的,这个函数会用之前设置的清除颜色填充整个颜色缓冲区
     */
    glClearColor(0.2f, 0.3f, 0.3f, 0.0f);
}

void GLWidget::resizeGL(int w, int h)
{
    /* resizeGL(int w, int h) 是 QOpenGLWidget 中的一个关键函数,
     * 专门用于处理窗口大小变化时的OpenGL视口(Viewport)和投影矩阵的调整。
     * 它的核心作用是 确保OpenGL渲染内容能正确适应窗口尺寸的变化。
     */
    glViewport(0, 0, w, h);//定义OpenGL渲染的可视区域(视口)
    /*前两个参数 (0, 0):视口左下角在窗口中的坐标(通常是窗口左下角)。
      后两个参数 (w, h):视口的宽度和高度(一般等于窗口的新尺寸)。
      效果:当窗口大小改变时,OpenGL会将渲染内容拉伸或压缩到新的视口范围内。
      如果不调用 glViewport,窗口尺寸变化时,渲染内容可能会:
      保持原比例导致黑边(未填满窗口)。
      拉伸变形(未正确适配新尺寸)。
      它确保了OpenGL坐标系到屏幕坐标系的正确映射。
      使用:1、窗口大小调整--用户拖动窗口边缘时,Qt会自动调用此函数。例如:从800x600调整为1024x768时,w 和 h 会更新为新值。
           2、初始化时--在initializeGL()之后,Qt会首次调用resizeGL()设置初始视口。
    */
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 1. 清除颜色缓冲区和深度缓冲区
    // 2. 设置投影矩阵
    glMatrixMode(GL_PROJECTION);//切换到投影矩阵栈
    glLoadIdentity();//重置当前矩阵为单位矩阵
    glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);//设置正交投影
    // 3. 设置模型视图矩阵
    glMatrixMode(GL_MODELVIEW);// 切换到模型视图矩阵栈
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    // 4、应用模型变换
    glTranslatef(trianglePos.x(), trianglePos.y(), 0.0f);//平移
    glRotatef(rotationAngle, 0.0f, 0.0f, 1.0f);//旋转
    // 5、绘制三角形
    glBegin(GL_TRIANGLES);

    glColor3f(1.0f, 0.0f, 0.0f);//第一个顶点(红色)
    glVertex2f(-0.2f, -0.2f);//设置顶点坐标

    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex2f(0.2f, -0.2f);

    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex2f(0.0f, 0.2f);

    glEnd();
}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        isDragging = true;
        lastMousePos = event->pos();
    }
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (isDragging) {
        // 计算鼠标移动的增量
        QPointF delta = event->pos() - lastMousePos;
        lastMousePos = event->pos();

        // 将屏幕坐标转换为OpenGL坐标
        // 注意Y轴方向相反
        float glDeltaX = 2.0f * delta.x() / width();
        float glDeltaY = -2.0f * delta.y() / height();

        // 更新三角形位置
        trianglePos += QPointF(glDeltaX, glDeltaY);

        update();
    }
}

void GLWidget::wheelEvent(QWheelEvent *event)
{
    // 获取滚轮角度增量
    QPoint angleDelta = event->angleDelta();

    // 通常垂直滚轮数据在angleDelta.y()
    if (!angleDelta.isNull()) {
        rotationAngle += angleDelta.y() * 0.1f; // 调整旋转速度
    //向上滚动:顺时针|向下滚动:逆时针
        // 规范化角度到0-360范围
        if (rotationAngle > 360.0f) rotationAngle -= 360.0f;
        if (rotationAngle < 0.0f) rotationAngle += 360.0f;

        update();
    }
}

void GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        isDragging = false;
    }
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle("OpenGL_1");
    resize(600, 600);

    GLWidget *glWidget = new GLWidget(this);
    setCentralWidget(glWidget);
}

MainWindow::~MainWindow()
{
}
#include "mainwindow.h"
#include <QApplication>
#include "glwidget.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值