在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中实现简单粒子效果的方法及相关示例。
