没有使用transformfeedback的粒子系统

注意:这是一个十分基础的粒子系统,没有billboard没有几何着色器没有光照没有噪声处理,算是粒子系统的一个小基础

上周通过OpenGL编程指南用TransFormFeedback技术按照书上那不完全的代码补全了粒子碰撞效果,然后我本意是想做一个火球效果。。。。。结果我觉得我还是先一步步来做一个基础的粒子系统。

粒子系统的主要成分就是一个粒子发射器类,我们写好该类后,可以通过调整参数实现各种不同的效果。
首先是粒子结构体:

struct Particle 
{
    vec3 Position, Velocity;//定义粒子的位置和速度
    vec4 Color;//定义粒子颜色
    GLfloat Life;//定义粒子生命周期

    Particle() : Position(0.0f), Velocity(0.0f), Color(1.0f), Life(0.0f) { }
};

然后是粒子发射器类:

class ParticleGenerator
{
public:
    ParticleGenerator(Shader shader, GLuint texture, GLuint amount, vec3 gravity);
    void Update(GLfloat dt, vec3 position, vec3 velocity, GLuint newParticles, vec3 offset = vec3(0.0f, 0.0f, 0.0f));//更新粒子状态
    void Draw(mat4 projection, mat4 view, mat4 model);//绘制粒子
private:
    vector<Particle> particles;//粒子数组,保存每个粒子的信息
    GLuint amount;
    Shader shader;
    GLuint texture;
    vec3 gravity;
    GLuint VAO;
    void init();//将粒子进行初始化
    GLuint firstUnusedParticle();//查询死亡的粒子
    void respawnParticle(Particle &particle, vec3 position,vec3 velocity, vec3 offset = vec3(0.0f, 0.0f, 0.0f));//将死亡的粒子重新初始化
};

定义完后,我们接下来就开始写各个函数:
1、粒子发射器的构造函数

ParticleGenerator::ParticleGenerator(Shader shader, GLuint texture, GLuint amount, vec3 gravity): shader(shader), texture(texture), amount(amount),gravity(gravity)
{
    this->init();
}

2、init函数,该函数用于具体设置粒子的形状

GLuint VBO;
    GLfloat particle_quad[] = 
    {
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 
        0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,         
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,  
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,  
        -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 
        0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,  
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,  
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 
        -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,  
        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,  
        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 
        -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,  
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 
        -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,  
        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 
        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 
        0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,    
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,  
        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,  
        0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 
        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 
        0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 
        -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 
        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 
        0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f 
    };//小立方体的顶点数据
    glGenVertexArrays(1, &this->VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(this->VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(particle_quad), particle_quad, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glBindVertexArray(0);

    for (GLuint i = 0; i < this->amount; ++i)
    {
        this->particles.push_back(Particle());
    }

3、Update函数

void ParticleGenerator::Update(GLfloat dt, vec3 position, vec3 velocity, GLuint newParticles, vec3 offset)
{
    //我当前想要生成newParticles个新粒子,我首先要查询是否有旧粒子死亡,为了防止生成大量数据
    for (GLuint i = 0; i < newParticles; ++i)
    {
        int unusedParticle = this->firstUnusedParticle();//获取已经死亡的旧粒子的下标
        if (unusedParticle != -1)
        {
            this->respawnParticle(this->particles[unusedParticle], position, velocity, offset);//重新生成该粒子
        }
    }
    for (GLuint i = 0; i < this->amount; ++i)//更新每个粒子的状态
    {
        Particle &p = this->particles[i];
        p.Life -= dt * 0.05; //减掉寿命

        if (p.Life > 0.0f)//没死的话
        {   
            p.Velocity = p.Velocity + dt * this->gravity;//更新速度
            p.Position += p.Velocity * dt;//更新位置
            p.Color.a = p.Life;//更新颜色
        }
    }
}

4、Draw函数

void ParticleGenerator::Draw(mat4 projection, mat4 view, mat4 model)
{
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);//开启融混

    this->shader.Use();
    for (vector<Particle>::iterator it = this->particles.begin(); it != this->particles.end(); it++)
    {
        if (it->Life > 0.0f)
        {
            glUniform3f(glGetUniformLocation(this->shader.Program, "offset"), it->Position.x, it->Position.y, it->Position.z);
            //传递坐标更改值,因为我暂时没用Transformfeedback,所以用了累计函数的形式进行更新
            glUniform4f(glGetUniformLocation(this->shader.Program, "color"), it->Color.x, it->Color.y, it->Color.z, it->Color.w);
            //传递颜色更改值
            glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "projection"), 1, GL_FALSE, value_ptr(projection));
            glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "view"), 1, GL_FALSE, value_ptr(view));
            glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "model"), 1, GL_FALSE, value_ptr(model));

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, this->texture);
            glBindVertexArray(this->VAO);
            glDrawArrays(GL_TRIANGLES, 0, 36);
            glBindVertexArray(0);
        }
    }
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//将融混模式调回默认状态
}

5、firstUnusedParticle函数,获取最先死亡的粒子

GLuint ParticleGenerator::firstUnusedParticle()
{
    //因为粒子随着时间生成,所以我们可以用一个lastUsedParticle记录上一个死亡的粒子的下标,因为下一个死亡的粒子的下标往往在上一个死亡粒子的后面
    for (GLuint i = lastUsedParticle; i < this->amount; ++i) 
    {
        if (this->particles[i].Life <= 0.0f) 
        {
            lastUsedParticle = i;
            return i;
        }
    }
    //如果没有找到的话,我们再找前面是否有粒子死亡
    for (GLuint i = 0; i < lastUsedParticle; ++i) 
    {
        if (this->particles[i].Life <= 0.0f) 
        {
            lastUsedParticle = i;
            return i;
        }
    }
    lastUsedParticle = 0;
    return -1;
}

6、respawnParticle函数

void ParticleGenerator::respawnParticle(Particle &particle, vec3 position,vec3 velocity, vec3 offset)
{
    GLfloat random1 = ((rand() % 100) - 50) / 10.0f;
    GLfloat random2 = ((rand() % 100) - 50) / 10.0f;
    GLfloat rColor = 0.5 + ((rand() % 100) / 100.0f);
    particle.Position = position;//定义初始位置
    particle.Color = vec4(rColor, rColor, rColor, 1.0f);//定义初始颜色
    particle.Life = 1.0f;//定义生命值大小
    vec3 direction = normalize(vec3(random1, 0.0f, random2));
    particle.Velocity = (velocity + direction * 100.0f + offset) * 0.1f;//定义初始速度
}

最后我们需要在main函数中调用:

ParticleGenerator *particle = new ParticleGenerator(particleshader, textureofparticle, 10000, vec3(0.0f, -9.8f, 0.0f));

particle->Update(deltaTime, vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 500.0f, 0.0f), 2, vec3(0.0f));
//参数分别为距离绘制上一帧的时间差,发射器初始位置,粒子初始速度,更新粒子的数量,发射器范围大小(类比于枪械的口径)
particle->Draw(projection, camera.GetViewMatrix(), model);

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值