【qt小系统】使用qt实现透明的3D魔方效果

在这里插入图片描述

摘要:本文主要是利用 QOpenGLWidget
实现的3D魔方效果,可实现自动宣传及鼠标拖动,可根据自己喜好贴不同的背景图及编写内容。代码实现了一个带有旋转立方体的 OpenGL
小部件,立方体的六个面上分别绘制了一些文字。

由于本人能力有限,难免有疏漏之处。

文中源码文件【获取方式】:关注公众号:利哥AI实例探险,

给公众号发送 “qt3D魔方” 获取下载方式,关注发送关键字即可获取下载方式,免费,无套路,关注即可!

原文地址:【qt小系统】使用qt实现透明的3D魔方效果

系统演示

在这里插入图片描述

基本介绍

  • 初始化时,代码加载立方体几何形状和纹理,准备好 OpenGL 的基本配置和着色器。
  • 鼠标移动控制立方体的旋转,通过捕获鼠标事件计算旋转轴和速度。
  • 使用定时器逐步降低旋转速度,模拟摩擦力的效果。
  • 在每一帧中,代码重新计算并设置投影矩阵,绑定纹理,并最终绘制立方体。
  • 立方体表面的纹理图像由 InitCube() 动态生成,使用 QPainter 绘制带有随机颜色和字体的文字。
    通过这些技术和逻辑,代码实现了一个可以根据鼠标输入旋转的立方体,每一面都显示了不同的图像和文字。
  1. 使用 initializeGL() 方法初始化 OpenGL 上下文,包括设置深度测试和背面剔除。
  2. 使用 paintGL() 方法进行绘制,负责绘制立方体的每一帧画面。
  3. 通过重载 mousePressEvent 和 mouseMoveEvent 方法,代码捕获并处理鼠标按下和移动事件,以计算并更新立方体的旋转角度。
  4. 计算鼠标移动的方向和距离,将其转化为旋转轴和旋转速度。
  5. 使用 timerEvent() 方法每隔一定时间触发一次,逐步减少旋转速度,实现类似摩擦力的效果,从而使立方体旋转逐渐停止。
  6. 使用 initShaders() 方法加载和编译顶点着色器和片段着色器,构建并绑定着色器程序。这一步为后续在 OpenGL 管线中使用着色器进行绘制做了准备。
  7. 使用 initTextures() 方法加载纹理,将其绑定到立方体的各个面。设置纹理的缩小和放大过滤模式,以及纹理的包裹模式(Repeat)。
  8. 使用 GeometryEngine 类管理立方体的几何形状,并在 paintGL() 方法中调用其 rawCubeGeometry() 方法绘制立方体。
  9. InitCube() 方法将预定义的文字转换为图像,并将多个图像合并成一个纹理,用于立方体的表面。使用 QPainter 类在图像上绘制文字,文字的字体和颜色是随机生成的。
  10. 使用 QMatrix4x4 进行模型视图变换和透视投影计算,在 resizeGL() 和 paintGL() 方法中设置合适的投影矩阵。

核心代码

在这里插入图片描述
vshader.glsl文件

#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif

uniform mat4 mvp_matrix;

attribute vec4 a_position;
attribute vec2 a_texcoord;

varying vec2 v_texcoord;

//! [0]
void main()
{
    // Calculate vertex position in screen space
    gl_Position = mvp_matrix * a_position;

    // Pass texture coordinate to fragment shader
    // Value will be automatically interpolated to fragments inside polygon faces
    v_texcoord = a_texcoord;
}
//! [0]

fshader.glsl文件

#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif

uniform sampler2D texture;

varying vec2 v_texcoord;

//! [0]
void main()
{
    // Set fragment color from texture
    gl_FragColor = texture2D(texture, v_texcoord);
}
//! [0]

主要部分代码,代码比较长,有需要的可以到文章摘要中获取下载方式,免费下载使用。

#include "DlgCube.h"


#include <QMouseEvent>
#include <math.h>
#include <QPen>
#include <QPainter>
#include <QTime>


DlgCube::DlgCube(int width, int height, QWidget *parent) :
    QOpenGLWidget(parent),
    geometries(0),
    texture(0),
    angularSpeed(0)
{
    this->setFixedSize(width, height);
}


DlgCube::~DlgCube()
{
    // Make sure the context is current when deleting the texture
    // and the buffers.
    makeCurrent();
    delete texture;
    delete geometries;
    doneCurrent();
}


void DlgCube::mousePressEvent(QMouseEvent *e)
{
    // Save mouse press position
    mousePressPosition = QVector2D(e->localPos());
}


void DlgCube::mouseMoveEvent(QMouseEvent *e)
{
    //qDebug()<<e->pos();
    // Mouse release position - mouse press position
    QVector2D diff = QVector2D(e->localPos()) - mousePressPosition;
    // Rotation axis is perpendicular to the mouse position difference
    // vector
    QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
    // Accelerate angular speed relative to the length of the mouse sweep
    qreal acc = diff.length() / 100.0;
    // Calculate new rotation axis as weighted sum
    rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
    // Increase angular speed
    angularSpeed += acc;

    mousePressPosition = QVector2D(e->localPos());
}

void DlgCube::timerEvent(QTimerEvent *)
{
    // Decrease angular speed (friction)
    angularSpeed *= 0.95;
    // Stop rotation when speed goes below threshold
    if (angularSpeed < 0.01) {
        angularSpeed = 0.0;
    } else {
        // Update rotation
        rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
        // Request an update
        update();
    }
}

void DlgCube::initializeGL()
{
    initializeOpenGLFunctions();
    //球 后 widget 的背景色
//    glClearColor(1, 1, 1, 0);
    initShaders();
    initTextures();
    // Enable depth buffer
    glEnable(GL_DEPTH_TEST);
    // Enable back face culling
    glEnable(GL_CULL_FACE);
    geometries = new GeometryEngine;
    // Use QBasicTimer because its faster than QTimer
    timer.start(12, this);
}


void DlgCube::initShaders()
{
    // Compile vertex shader
    if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl"))
        close();
    // Compile fragment shader
    if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl"))
        close();
    // Link shader pipeline
    if (!program.link())
        close();
    // Bind shader pipeline for use
    if (!program.bind())
        close();
}


void DlgCube::initTextures()
{
    // Load cube.png image
    texture = new QOpenGLTexture(myImage.mirrored());
    // Set nearest filtering mode for texture minification
    texture->setMinificationFilter(QOpenGLTexture::Nearest);

    // Set bilinear filtering mode for texture magnification
    texture->setMagnificationFilter(QOpenGLTexture::Linear);
    // Wrap texture coordinates by repeating
    // f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
    texture->setWrapMode(QOpenGLTexture::Repeat);
}


void DlgCube::resizeGL(int w, int h)
{
    // Calculate aspect ratio
    qreal aspect = qreal(w) / qreal(h ? h : 1);


    // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
    //fov 可以调节大小
    const qreal zNear = 2.5, zFar = 7.0, fov = 45.0;


    // Reset projection
    projection.setToIdentity();


    // Set perspective projection
    projection.perspective(fov, aspect, zNear, zFar);
}


void DlgCube::paintGL()
{
    // Clear color and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    texture->bind();
    // Calculate model view transformation
    QMatrix4x4 matrix;
    matrix.translate(0.0, 0.0, -5.0);
    matrix.rotate(rotation);
    // Set modelview-projection matrix
    program.setUniformValue("mvp_matrix", projection * matrix);
    // Use texture unit 0 which contains cube.png
    program.setUniformValue("texture", 0);
    // Draw cube geometry
    geometries->drawCubeGeometry(&program);
}


void DlgCube::InitCube()
{
    m_which_color = 0;
    tip_1.clear(); tip_2.clear(); tip_3.clear(); tip_4.clear(); tip_5.clear(); tip_6.clear();
    tip_1.push_back("<静夜思>");
    tip_1.push_back("床前明月光");
    tip_1.push_back("疑是地上霜");
    tip_1.push_back("举头望明月");
    tip_1.push_back("低头思故乡");


    tip_2.push_back("<江雪>");
    tip_2.push_back("千山鸟飞绝");
    tip_2.push_back("万径人踪灭");
    tip_2.push_back("孤舟蓑笠翁");
    tip_2.push_back("独钓寒江雪");


    tip_3.push_back("<闻乐天授江州司马>");
    tip_3.push_back("残灯无焰影幢幢");
    tip_3.push_back("此夕闻君谪九江");
    tip_3.push_back("垂死病中惊坐起");
    tip_3.push_back("暗风吹雨入寒窗");


    tip_4.push_back("问世间情是何物");
    tip_4.push_back("直教生死相许");
    tip_4.push_back("天南地北双飞客");
    tip_4.push_back("老翅几回寒暑");
    tip_4.push_back("欢乐趣");


    tip_5.push_back("离别苦");
    tip_5.push_back("就中更有痴儿女");
    tip_5.push_back("君应有语");


    tip_6.push_back("渺万里层云,千山暮雪,只影向谁去?");
    //merge
    QString str = QString(":image/edge.png");
    QImage resultImg(str);
    //
    QPainter painter;
    painter.begin(&resultImg);
    //在新区域画图
    painter.drawImage(  0,   0, Text_To_Image(0));
    painter.drawImage(396,   0, Text_To_Image(1));
    painter.drawImage(792,   0, Text_To_Image(2));
    painter.drawImage(  0, 400, Text_To_Image(3));
    painter.drawImage(396, 400, Text_To_Image(4));
    painter.drawImage(792, 400, Text_To_Image(5));
    painter.end();
    QString strResultImg = QString("./tiledball.png");
    resultImg.save(strResultImg);
    myImage = resultImg;
}


void DlgCube::MoveLittle()
{
    //move 初始位置露出一个角
    QVector2D diff = QVector2D(QPoint(180,180));
    QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
    qreal acc = diff.length() / 100.0;
    rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
    angularSpeed += acc;
}


QImage DlgCube::Text_To_Image(int count)
{
    QVector <QString> tip;
    QImage img;
    switch(count)
    {
    case 0:tip = tip_1; img.load(":image/11.png");break;
    case 1:tip = tip_2; img.load(":image/22.png");break;
    case 2:tip = tip_3; img.load(":image/33.png");break;
    case 3:tip = tip_4; img.load(":image/44.png");break;
    case 4:tip = tip_5; img.load(":image/55.png");break;
    case 5:tip = tip_6; img.load(":image/66.png");break;
    default:break;
    }
    QPainter painter;
    painter.begin(&img);


    QFont font;
    font.setBold(true);


    int tip_size = tip.size()>8?8:tip.size();
    for(int i=0;i<tip_size;i++)
    {
        switch (m_which_color%2)
        {
        case 0:font.setFamily("Noto Sans S Chinese Thin");break;
        case 1:font.setFamily("Noto Sans S Chinese Bold");break;
        default:break;
        }
        font.setPixelSize(random_fontpixsize(tip[i].length()));
        painter.setFont(font);
        QPen pen = random_Color();
        painter.setPen(pen);
        //左右各留20px
        int x = 20 + qrand()%((360 - tip[i].length()*font.pixelSize())+1);
        if(x<0)
            x = 5;
        //y会在实际图上向下偏移10个坐标
        int y = 380/tip_size*i + qrand()%((380/tip_size - font.pixelSize())+1);
        painter.drawText(QRect(x, y, 380,380), Qt::AlignLeft|Qt::AlignTop,tip[i]);
    }


    painter.end();
    return img;
}


QPen DlgCube::random_Color()
{
    QPen pen;
    switch (m_which_color % 4) {
    case 0:pen.setColor(QColor(151,255,244));
        break;
    case 1:pen.setColor(QColor(255,195,109));
        break;
    case 2:pen.setColor(QColor(124,204,255));
        break;
    case 3:pen.setColor(QColor(255,255,255));
        break;
    default:
        break;
    }
    m_which_color++;
    return pen;
}


int DlgCube::random_fontpixsize(int fontlen)
{
    if(0 == fontlen)
        return 1;
    int size = (380 / fontlen / 2) + qrand()%(380 / fontlen / 2);
    //字体大小 25<size<38
    if (size > 38)
        size = 33 + qrand() % 6;


    if(size < 25)
        size = 25 + qrand() % 6;


    return  size;
}

往期文章回顾

【深度学习】物体检测/实例分割/物体追踪/姿态估计/定向边框/图像分类检测演示系统【含源码】【深度学习】YOLOV8数据标注及模型训练方法整体流程介绍及演示
【深度学习】行人跌倒行为检测软件系统【深度学习】火灾检测软件系统
【深度学习】吸烟行为检测软件系统【深度学习】数竹签演示软件系统
【深度学习】菜品目标检测软件系统QT5集成FTP实现文件及文件夹的下载
QT集成开源日志库示例python源码加密之Cython方案简单示例
【python源码加密】Cython针对python工程多层级目录处理办法http服务网络请求如何确保数据安全(含python示例源码)
http请求数据传输时确保完整性和保密性方案(含源码)QT集成百度在线地图JS API实现交互及特定效果
【qt小游戏】小老鼠闯迷宫小游戏(附源码)【qt小系统】传感器云平台3D散点图(附源码)
【qt小系统】通过qt折线图实现论文内容-快餐店排队效能分析【qt小系统】使用qt实现透明的3D魔方效果

结束语

由于本人能力有限,难免有疏漏之处!
文中源码文件【获取方式】:关注公众号:利哥AI实例探险
给公众号发送 “qt3D魔方” 获取下载方式,免费,无套路,关注即可!!!

原文地址:【qt小系统】使用qt实现透明的3D魔方效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

利哥AI实例探险

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值