opengl编写简单粒子系统
opengl实现简单粒子系统
定义一个粒子结构Particle
Particle包含三个属性,分别是位置position,速度velocity,以及重量mass
struct Particle {
float position[3];
float velocity[3];
float mass;
};
创建了一个存储粒子的容器 particles,用于存储生成的粒子
使用vector来存储粒子,用数组也可以,使用vector记得导入vector库函数
std::vector<Particle> particles; // 粒子容器
定义了场景的宽度和高度
这个即为设置的场景宽度,粒子碰撞到场景之后会弹回
float sceneWidth = 2.0f;
float sceneHeight = 2.0f;
编写了 initParticle 函数,用于初始化单个粒子的属性。
初始化将粒子的位置速度都为0,粒子质量为1
void initParticle(Particle& particle) {
particle.position[0] = 0.0f;
particle.position[1] = 0.0f;
particle.position[2] = 0.0f;
particle.velocity[0] = 0.0f;
particle.velocity[1] = 0.0f;
particle.velocity[2] = 0.0f;
particle.mass = 1.0f; // 设置粒子质量
}
编写了 renderScene 函数,用于绘制粒子
这段代码用来绘制粒子,glcolor3f决定了绘制的粒子的颜色,glutsolidSphere决定了绘制的粒子的形状,可根据需要在这里更改粒子的大小颜色形状等属性
void renderScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// 绘制粒子
for (const auto& particle : particles) {
glPushMatrix();
glTranslatef(particle.position[0], particle.position[1], particle.position[2]);
glColor3f(1.0f, 1.0f, 1.0f);
glutSolidSphere(0.1f, 10, 10);
glPopMatrix();
}
glutSwapBuffers();
}
编写了 launchParticle 函数,用于每隔一秒发射一个粒子并将其添加到粒子容器中
粒子发射器,glutTimerFunc决定粒子的发射间隔
void launchParticle(int value) {
Particle particle;
initParticle(particle);
particles.push_back(particle); // 生成新粒子并添加到容器中
glutTimerFunc(1000, launchParticle, 0); // 1秒后再次发射粒子
}
编写了 mainLoop 函数作为主循环,用于更新粒子状态和进行碰撞检测。
主循环,这里设置的其他力为沿着x轴正方向的9.8f大小的力,与重力一致,计算合理,更新粒子的速度,位置并进行碰撞检测
void mainLoop(int value) {
clock_t currentTime = clock();
static clock_t lastFrameTime = currentTime;
float deltaTime = static_cast<float>(currentTime - lastFrameTime) / CLOCKS_PER_SEC;
// 更新粒子状态
for (auto& particle : particles) {
// 在这里计算粒子的合力、更新速度和位移
// ...
float gravity = 9.8f; // 重力加速度
// 分解合力为重力和其他力
float forceGravity[3] = { 0.0f, -particle.mass * gravity, 0.0f };
float forceOther[3] = { 9.8f, 0.0f, 0.0f };
// 计算合力f
float force[3];
force[0] = forceGravity[0] + forceOther[0];
force[1] = forceGravity[1] + forceOther[1];
force[2] = forceGravity[2] + forceOther[2];
// 更新速度v
float acceleration[3];
acceleration[0] = force[0] / particle.mass;
acceleration[1] = force[1] / particle.mass;
acceleration[2] = force[2] / particle.mass;
particle.velocity[0] += acceleration[0] * deltaTime;
particle.velocity[1] += acceleration[1] * deltaTime;
particle.velocity[2] += acceleration[2] * deltaTime;
// 更新位移x
particle.position[0] += particle.velocity[0] * deltaTime;
particle.position[1] += particle.velocity[1] * deltaTime;
particle.position[2] += particle.velocity[2] * deltaTime;
// 碰撞检测
if (particle.position[0] >= sceneWidth || particle.position[0] <= -sceneWidth) {
particle.velocity[0] *= -1.0f; // x方向速度反向
}
if (particle.position[1] >= sceneHeight || particle.position[1] <= -sceneHeight) {
particle.velocity[1] *= -1.0f; // y方向速度反向
}
// 更新粒子的位置
particle.position[0] += particle.velocity[0] * deltaTime;
particle.position[1] += particle.velocity[1] * deltaTime;
}
lastFrameTime = currentTime;
glutPostRedisplay(); // 触发重绘
glutTimerFunc(16, mainLoop, 0); // 16ms刷新一次,约60帧每秒
}
在 main 函数中进行 OpenGL 的初始化,并注册绘制函数、启动粒子发射和主循环
主函数,进行上述代函数的调用
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800, 600);
glutCreateWindow("Particle System");
glutDisplayFunc(renderScene); // 注册绘制函数
launchParticle(0); // 开始发射粒子
glutTimerFunc(0, mainLoop, 0); // 启动主循环
glutMainLoop();
return 0;
}
程序运行结果
由于设置了一个重力,又设置了一个x轴正方向的力想右,且大小等于重力=9.8f,所以运行结果的粒子会向右下角移动,并且由于碰撞,会改变方向,反弹回来
完整代码
#include <GL/glut.h>
#include <cmath>
#include <ctime>
#include <vector>
// 定义粒子结构
struct Particle {
float position[3];
float velocity[3];
float mass;
};
std::vector<Particle> particles; // 粒子容器
float sceneWidth = 2.0f;
float sceneHeight = 2.0f;
// 初始化粒子
void initParticle(Particle& particle) {
particle.position[0] = 0.0f;
particle.position[1] = 0.0f;
particle.position[2] = 0.0f;
particle.velocity[0] = 0.0f;
particle.velocity[1] = 0.0f;
particle.velocity[2] = 0.0f;
particle.mass = 1.0f; // 设置粒子质量
}
// 渲染函数
void renderScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// 绘制粒子
for (const auto& particle : particles) {
glPushMatrix();
glTranslatef(particle.position[0], particle.position[1], particle.position[2]);
glColor3f(1.0f, 1.0f, 1.0f);
glutSolidSphere(0.1f, 10, 10);
glPopMatrix();
}
glutSwapBuffers();
}
// 发射粒子
void launchParticle(int value) {
Particle particle;
initParticle(particle);
particles.push_back(particle); // 生成新粒子并添加到容器中
glutTimerFunc(1000, launchParticle, 0); // 1秒后再次发射粒子
}
// 主循环
void mainLoop(int value) {
clock_t currentTime = clock();
static clock_t lastFrameTime = currentTime;
float deltaTime = static_cast<float>(currentTime - lastFrameTime) / CLOCKS_PER_SEC;
// 更新粒子状态
for (auto& particle : particles) {
// 在这里计算粒子的合力、更新速度和位移
// ...
float gravity = 9.8f; // 重力加速度
// 分解合力为重力和其他力
float forceGravity[3] = { 0.0f, -particle.mass * gravity, 0.0f };
float forceOther[3] = { 9.8f, 0.0f, 0.0f };
// 计算合力f
float force[3];
force[0] = forceGravity[0] + forceOther[0];
force[1] = forceGravity[1] + forceOther[1];
force[2] = forceGravity[2] + forceOther[2];
// 更新速度v
float acceleration[3];
acceleration[0] = force[0] / particle.mass;
acceleration[1] = force[1] / particle.mass;
acceleration[2] = force[2] / particle.mass;
particle.velocity[0] += acceleration[0] * deltaTime;
particle.velocity[1] += acceleration[1] * deltaTime;
particle.velocity[2] += acceleration[2] * deltaTime;
// 更新位移x
particle.position[0] += particle.velocity[0] * deltaTime;
particle.position[1] += particle.velocity[1] * deltaTime;
particle.position[2] += particle.velocity[2] * deltaTime;
// 碰撞检测
if (particle.position[0] >= sceneWidth || particle.position[0] <= -sceneWidth) {
particle.velocity[0] *= -1.0f; // x方向速度反向
}
if (particle.position[1] >= sceneHeight || particle.position[1] <= -sceneHeight) {
particle.velocity[1] *= -1.0f; // y方向速度反向
}
// 更新粒子的位置
particle.position[0] += particle.velocity[0] * deltaTime;
particle.position[1] += particle.velocity[1] * deltaTime;
}
lastFrameTime = currentTime;
glutPostRedisplay(); // 触发重绘
glutTimerFunc(16, mainLoop, 0); // 16ms刷新一次,约60帧每秒
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800, 600);
glutCreateWindow("Particle System");
glutDisplayFunc(renderScene); // 注册绘制函数
launchParticle(0); // 开始发射粒子
glutTimerFunc(0, mainLoop, 0); // 启动主循环
glutMainLoop();
return 0;
}