粒子系统—雪景模拟
1. 粒子系统
粒子系统是用来模拟微小粒子的物理学模型,一般用于火焰、爆炸等由微小粒子组成的物理现象的模拟。一般而言,粒子系统由大量具有颜色、位置、速度等特征的粒子组成。通常用于游戏中的特效或者雨雪、火焰等模拟。
2.粒子的属性:
- 位置
- 速度
- 颜色
- 声明周期
- 重力
一般而言粒子具有以上的属性,但是可以根据具体的情况来增加或者减少一些属性,还可以给粒子加上其他物理特性,如可以让落在地面的粒子反弹等。
3.下雪效果的粒子系统
这里给出一个opengl实现的模拟下雪效果的粒子系统,具体代码如下:
// particles.h
#ifndef OPENGLUS_EXAMPLES_PARTICLES_H
#define OPENGLUS_EXAMPLES_PARTICLES_H
#include <vector>
#include <random>
#include "instance.h"
#include "program.h"
#include "glm/glm.hpp"
namespace openglus {
struct Particle {
public:
Particle( )
: offset(area_randomer(e), 200.0f, area_randomer(e)),
speed(0.0f, speed_randomer(e), 0.0f), size(size_randomer(e))
{ }
public:
static std::default_random_engine e;
static std::uniform_real_distribution<double> speed_randomer;
static std::uniform_int_distribution<unsigned> size_randomer;
static std::uniform_real_distribution<double> area_randomer;
public:
glm::vec3 offset, speed;
GLfloat size;
};
class ParticleSystem {
public:
ParticleSystem(GLuint max_particles);
void Update(GLfloat time_passed, const glm::vec3& camera_pos);
void Draw(Program& program);
private:
void Init( );
void BufferData();
private:
GLuint nums_;
std::vector<Particle> particles_;
Instance<GLfloat> snow_instance_;
std::vector<glm::vec3> offset_;
std::vector<GLfloat> size_;
};
} // namespace openglus
#endif // OPENGLUS_EXAMPLES_PARTICLES_H
在该粒子系统中,雪粒子只有位置、大小与速度不同,其他属性都被忽略,对于雪粒子,在opengl中使用GL_POINTS模式绘制,并且给粒子贴上雪的纹理,采用instance形式绘制,会提高效率。
// particles.cc
#include "particles.h"
#include <algorithm>
#include <random>
#include <cstddef>
#include "gl/glew.h"
namespace openglus {
std::default_random_engine Particle::e;
std::uniform_real_distribution<double> Particle::speed_randomer(2.0, 5.0);
std::uniform_int_distribution<unsigned> Particle::size_randomer(3, 10);
std::uniform_real_distribution<double> Particle::area_randomer(-100, 100);
ParticleSystem::ParticleSystem(GLuint max_particles)
: nums_(max_particles), particles_(max_particles), snow_instance_({0.0f, 0.0f, 0.0f})
{
Init( );
}
void ParticleSystem::Init( )
{
snow_instance_.SetBaseMeshAttribute(0, 3, GL_FLOAT, 3 * sizeof(GLfloat));
snow_instance_.AddInstanceAttribute(nums_ * 3 * sizeof(GLfloat), 1, 3, GL_FLOAT, 3 * sizeof(GLfloat));
snow_instance_.AddInstanceAttribute(nums_ * sizeof(GLfloat), 2, 1, GL_FLOAT, sizeof(GLfloat));
}
void ParticleSystem::Update(GLfloat time_passed, const glm::vec3& camera_pos)
{
std::vector<glm::vec3> offset;
std::vector<GLfloat> size;
for (int i = 0; i < nums_; ++i) {
Particle& p = particles_[i];
if (p.offset.y > 0.0f) {
p.offset -= p.speed * time_passed * 5.0f;
offset.push_back(glm::vec3(p.offset));
size.push_back(p.size);
} else {
p = Particle( );
}
}
std::swap(offset_, offset);
std::swap(size_, size);
BufferData();
}
void ParticleSystem::BufferData()
{
snow_instance_.BufferStreamData(1, offset_.size( ) * 3 * sizeof(GLfloat), offset_.data( ));
snow_instance_.BufferStreamData(2, size_.size( ) * sizeof(GLfloat), size_.data( ));
}
void ParticleSystem::Draw(Program& program)
{
snow_instance_.DrawArray(program, 1, offset_.size( ), GL_POINTS);
}
} // namespace openglus