欢迎访问宙启技术站
智能推送

在QOpenGLWidget()中实现简单的粒子效果

发布时间:2024-01-02 10:10:17

在QOpenGLWidget中实现简单的粒子效果,可以使用OpenGL的点精灵来实现。点精灵是OpenGL中一种特殊的绘制方式,可以通过设置点的大小和颜色来模拟粒子效果。

首先需要在Qt的项目中添加OpenGL模块,可以在项目文件中添加以下代码:

QT += opengl

然后,创建一个继承自QOpenGLWidget的类,并重写其一些函数以实现粒子效果。下面是一个简单的例子:

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>

class ParticleWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    ParticleWidget(QWidget *parent = nullptr);
    ~ParticleWidget();

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;
    void timerEvent(QTimerEvent *event) override;

private:
    QOpenGLShaderProgram shaderProgram;
    QOpenGLBuffer vertexBuffer;
    int timerId;
    float time;

    void createParticles();
    void updateParticles();
};

ParticleWidget::ParticleWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    time = 0;

    // 启动定时器
    timerId = startTimer(16); // 16毫秒刷新一次,约60帧

    // 加载顶点着色器
    if (!shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader/particle.vert"))
        close();

    // 加载片段着色器
    if (!shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader/particle.frag"))
        close();

    // 链接着色器程序
    if (!shaderProgram.link())
        close();
}

ParticleWidget::~ParticleWidget()
{
    vertexBuffer.destroy();
}

void ParticleWidget::initializeGL()
{
    initializeOpenGLFunctions();

    // 初始化顶点缓冲区
    createParticles();
}

void ParticleWidget::paintGL()
{
    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    shaderProgram.bind();
    shaderProgram.setUniformValue("time", time);

    vertexBuffer.bind();
    shaderProgram.enableAttributeArray(0);
    shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 4, sizeof(float) * 4);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDrawArrays(GL_POINTS, 0, 1000);

    vertexBuffer.release();
    shaderProgram.release();
}

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

void ParticleWidget::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == timerId) {
        // 更新粒子
        updateParticles();

        // 更新时间
        time += 0.016; // 16毫秒

        // 重新绘制
        update();
    }
}

void ParticleWidget::createParticles()
{
    float particles[4000];
    for (int i = 0; i < 1000; ++i) {
        particles[i * 4] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);
        particles[i * 4 + 1] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);
        particles[i * 4 + 2] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);
        particles[i * 4 + 3] = 1.0;
    }

    vertexBuffer.create();
    vertexBuffer.bind();
    vertexBuffer.allocate(particles, sizeof(particles));
    vertexBuffer.release();
}

void ParticleWidget::updateParticles()
{
    // 更新粒子的位置和透明度
    float particles[4000];
    for (int i = 0; i < 1000; ++i) {
        particles[i * 4] += 0.01 * (rand() / (float)RAND_MAX - 0.5);
        particles[i * 4 + 1] += 0.01 * (rand() / (float)RAND_MAX - 0.5);
        particles[i * 4 + 2] += 0.01 * (rand() / (float)RAND_MAX - 0.5);

        if (particles[i * 4] > 1.0 || particles[i * 4] < -1.0)
            particles[i * 4] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);
        if (particles[i * 4 + 1] > 1.0 || particles[i * 4 + 1] < -1.0)
            particles[i * 4 + 1] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);
        if (particles[i * 4 + 2] > -0.5 || particles[i * 4 + 2] < -1.0)
            particles[i * 4 + 2] = -1.0 + 2.0 * (rand() / (float)RAND_MAX);

        particles[i * 4 + 3] = 0.5 + 0.5 * (rand() / (float)RAND_MAX);
    }

    vertexBuffer.bind();
    vertexBuffer.write(0, particles, sizeof(particles));
    vertexBuffer.release();
}

在上面的例子中,首先需要加载一个顶点着色器和一个片段着色器,并进行链接。然后使用一个定时器来更新粒子的位置和透明度,并在每次绘制时将粒子数据传递给OpenGL进行绘制。

顶点着色器的代码如下所示:

#version 330 core

layout (location = 0) in vec4 vertex;

uniform float time;

void main()
{
    gl_Position = vec4(vertex.xyz, 1.0);

    gl_PointSize = 10.0;
    gl_PointSize += sin(time * 5.0 + vertex.x * 10.0) * 2.0;
}

片段着色器的代码如下所示:

#version 330 core

out vec4 fragColor;

void main()
{
    float alpha = 1.0 - length(gl_PointCoord - vec2(0.5, 0.5));
    fragColor = vec4(1.0, 1.0, 1.0, alpha);
}

在片段着色器中,我们使用点精灵的坐标来计算点的透明度,使得点的中心透明度较高,边缘透明度较低,从而形成粒子的效果。

最后,将编译好的顶点着色器和片段着色器文件(particle.vert和particle.frag)添加到Qt的资源文件中。

可以使用以下代码在主窗口中使用粒子效果:

ParticleWidget *particleWidget = new ParticleWidget(this);
particleWidget->setFixedSize(400, 400);

以上就是在QOpenGLWidget中实现简单粒子效果的方法及相关示例。