【openGLES3.0编程指南笔记-11】粒子系统

概述

粒子的属性:

寿命 a_lifetime

开始位置 a_startPosition

结束位置 a_endPosition

中心位置 u_centerPosition

颜色 u_color随机生成

时间 u_time持续一秒钟

1000个粒子,在1秒钟的时间,从开始位置,线性移动到结束位置。

1. 初始化粒子的位置

int Init(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *)myesContext->userData;
    int i;

    char vShaderStr[] =
            "#version 300 es \n"
            "uniform float u_time; \n"
            "uniform vec3 u_centerPosition; \n"
            "layout(location = 0) in float a_lifetime; \n"
            "layout(location = 1) in float a_startPosition; \n"
            "layout(location = 2) in vec3 a_endPosition; \n"
            "out float v_lifetime; \n"
            "void main() \n"
            "{ \n"
            "   if ( u_time <= a_lifetime ) \n"
            "   {   \n"
        // 这个是线性插值
            "       gl_Position.xyz = a_startPosition + (u_time * a_endPosition); \n"
        // 移动中心位置
            "       gl_Position.xyz += u_centerPosition; \n"
            "       gl_Position.w = 1.0;    \n"
            "   } \n"
            "   else \n"
            "   { \n"
            "       gl_Position = vec4(-1000, -1000, 0, 0); \n"
            "   } \n"
            "   v_lifetime = 1.0 - (u_time / a_lifetime); \n"
            "   v_lifetime = clamp(v_lifetime, 0.0, 1.0); \n"
            "   gl_PointSize  = (v_lifetime * v_lifetime) * 40.0; \n"
            "}";
// 颜色
    char fShaderStr[] =
            "#version 300 es \n"
            "precision mediump float; \n"
            "uniform vec4 u_color; \n"
            "in float v_lifetime; \n"
            "layout(location = 0) out vec4 fragColor; \n"
            "uniform sampler2D s_texture; \n"
            "void main() \n"
            "{ \n"
            "   vec4 texColor; \n"
            "   texColor = texture(s_texture, gl_PointCoord); \n"
        // 乘于一个纹理的值
            "   fragColor = vec4(u_color) * texColor; \n"
        // 透明度根据时间,逐渐变暗
            "   fragColor.a *= v_lifetime; \n"
            "} \n";

    userData->programObject = myesLoadProgram(vShaderStr, fShaderStr);
    userData->timeLoc = glGetUniformLocation(userData->programObject, "u_time");
    userData->centerPositionLoc = glGetUniformLocation(userData->programObject, "u_centerPosition");
    userData->colorLoc = glGetUniformLocation(userData->programObject, "u_color");
    userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    srand(0);
    for (i = 0; i < NUM_PARTICLES; i++)
    {
        float *praticleData = &userData->particleData[i*PARTICLE_SIZE];
        // 取余10000,是将数据控制到10000以内,0-1之间,寿命a_lifetime
        (*praticleData++) = ((float)(rand()%10000)/10000.0f);
// -1到1之间,粒子的开始位置
        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;
        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;
        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;
// -0.125-0之间,粒子的最终位置
        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
    }
    userData->time = 1.0f;
    userData->textureId = LoadTexture(myesContext->platformData, "smoke.tga");
    if (userData->textureId <= 0) {
        return GL_FALSE;
    }

    return GL_TRUE;
}

2. 更新粒子的位置和颜色

void Update(MYESContext *myesContext, float deltaTime)
{
    myUserData *userData = (myUserData *)myesContext->userData;

    userData->time += deltaTime;
    glUseProgram(userData->programObject);
    // 要1秒之后才更新
    if (userData->time >= 1.0f)
    {
        float centerPos[3];
        float color[4];
        userData->time = 0.0f;
        // 中心位置,-0.5 到 0.5
        centerPos[0] = ((float) (rand()%10000) / 10000.0f) - 0.5f;
        centerPos[1] = ((float) (rand()%10000) / 10000.0f) - 0.5f;
        centerPos[2] = ((float) (rand()%10000) / 10000.0f) - 0.5f;

        glUniform3fv(userData->centerPositionLoc, 1, &centerPos[0]);
// 颜色,0.5到1.0
        color[0] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[1] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[2] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[3] = 0.5;

        glUniform4fv(userData->colorLoc, 1, &color[0]);
    }

    glUniform1f(userData->timeLoc, userData->time);
}

源码解析

#include "esUtil.h"
#include <stdlib.h>
#include <math.h>

#define NUM_PARTICLES 1000
#define PARTICLE_SIZE 7

#define ATTRIBUTE_LIFETIME_LOCATION 0
#define ATTRIBUTE_STARTPOSITION_LOCATION 1
#define ATTRIBUTE_ENDPOSITION_LOCATION 2
typedef struct
{
    GLuint programObject;
    GLint timeLoc;
    GLint colorLoc;
    GLuint centerPositionLoc;
    GLint samplerLoc;
    GLuint textureId;
    float particleData[NUM_PARTICLES*PARTICLE_SIZE];
    float time;
} myUserData;
GLuint LoadTexture(void *ioContext, char *fileName)
{
    int width, height;
    char *buffer = esLoadTGA(ioContext, fileName, &width, &height);
    GLuint texId;

    if (buffer == NULL) {
        esLogMessage("Error loading (%s) image.\n", fileName);
        return 0;
    }

    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    free(buffer);
    return texId;
}
int Init(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *)myesContext->userData;
    int i;

    char vShaderStr[] =
            "#version 300 es \n"
            "uniform float u_time; \n"
            "uniform vec3 u_centerPosition; \n"
        // 寿命
            "layout(location = 0) in float a_lifetime; \n"
        // 粒子开始位置
            "layout(location = 1) in float a_startPosition; \n"
        // 粒子结束位置
            "layout(location = 2) in vec3 a_endPosition; \n"
            "out float v_lifetime; \n"
            "void main() \n"
            "{ \n"
            "   if ( u_time <= a_lifetime ) \n"
            "   {   \n"
            "       gl_Position.xyz = a_startPosition + (u_time * a_endPosition); \n"
            "       gl_Position.xyz += u_centerPosition; \n"
            "       gl_Position.w = 1.0;    \n"
            "   } \n"
            "   else \n"
            "   { \n"
            "       gl_Position = vec4(-1000, -1000, 0, 0); \n"
            "   } \n"
            "   v_lifetime = 1.0 - (u_time / a_lifetime); \n"
            "   v_lifetime = clamp(v_lifetime, 0.0, 1.0); \n"
            "   gl_PointSize  = (v_lifetime * v_lifetime) * 40.0; \n"
            "}";

    char fShaderStr[] =
            "#version 300 es \n"
            "precision mediump float; \n"
            "uniform vec4 u_color; \n"
            "in float v_lifetime; \n"
            "layout(location = 0) out vec4 fragColor; \n"
            "uniform sampler2D s_texture; \n"
            "void main() \n"
            "{ \n"
            "   vec4 texColor; \n"
            "   texColor = texture(s_texture, gl_PointCoord); \n"
            "   fragColor = vec4(u_color) * texColor; \n"
            "   fragColor.a *= v_lifetime; \n"
            "} \n";

    userData->programObject = myesLoadProgram(vShaderStr, fShaderStr);
    userData->timeLoc = glGetUniformLocation(userData->programObject, "u_time");
    userData->centerPositionLoc = glGetUniformLocation(userData->programObject, "u_centerPosition");
    userData->colorLoc = glGetUniformLocation(userData->programObject, "u_color");
    userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    srand(0);
    for (i = 0; i < NUM_PARTICLES; i++)
    {
        float *praticleData = &userData->particleData[i*PARTICLE_SIZE];
        (*praticleData++) = ((float)(rand()%10000)/10000.0f);

        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;
        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;
        (*praticleData++) = ((float)(rand()%10000)/5000.0f) - 1.0f;

        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
        (*praticleData++) = ((float)(rand()%10000)/40000.0f) - 0.125f;
    }
    userData->time = 1.0f;
    userData->textureId = LoadTexture(myesContext->platformData, "smoke.tga");
    if (userData->textureId <= 0) {
        return GL_FALSE;
    }

    return GL_TRUE;
}
void Update(MYESContext *myesContext, float deltaTime)
{
    myUserData *userData = (myUserData *)myesContext->userData;

    userData->time += deltaTime;
    glUseProgram(userData->programObject);
    if (userData->time >= 1.0f)
    {
        float centerPos[3];
        float color[4];
        userData->time = 0.0f;
        centerPos[0] = ((float) (rand()%10000) / 10000.0f) - 0.5f;
        centerPos[1] = ((float) (rand()%10000) / 10000.0f) - 0.5f;
        centerPos[2] = ((float) (rand()%10000) / 10000.0f) - 0.5f;

        glUniform3fv(userData->centerPositionLoc, 1, &centerPos[0]);

        color[0] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[1] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[2] = ((float) (rand() % 10000) / 20000.0f) + 0.5f;
        color[3] = 0.5;

        glUniform4fv(userData->colorLoc, 1, &color[0]);
    }

    glUniform1f(userData->timeLoc, userData->time);
}
void Draw(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *) myesContext->userData;
    glViewport(0, 0, myesContext->width, myesContext->height);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(userData->programObject);
    glVertexAttribPointer(ATTRIBUTE_LIFETIME_LOCATION, 1, GL_FLOAT,
            GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), userData->particleData);
    glVertexAttribPointer(ATTRIBUTE_ENDPOSITION_LOCATION, 3, GL_FLOAT,
                          GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), &userData->particleData[1]);
    glVertexAttribPointer(ATTRIBUTE_STARTPOSITION_LOCATION, 3, GL_FLOAT,
                          GL_FALSE, PARTICLE_SIZE * sizeof(GLfloat), &userData->particleData[4]);

    glEnableVertexAttribArray(ATTRIBUTE_LIFETIME_LOCATION);
    glEnableVertexAttribArray(ATTRIBUTE_ENDPOSITION_LOCATION);
    glEnableVertexAttribArray(ATTRIBUTE_STARTPOSITION_LOCATION);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, userData->textureId);
    glUniform1i(userData->samplerLoc, 0);
    glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
}
void ShutDown(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *)myesContext->userData;
    glDeleteTextures(1, &userData->textureId);
    glDeleteProgram(userData->programObject);
}
int myesMain(MYESContext *myesContext)
{
    myesContext->userData = (myUserData *)malloc(sizeof(myUserData));
    myesCreateWindow(myesContext, "PaticleSystem", 640, 480, MY_ES_WINDOW_RGB);
    if (!Init(myesContext)) {
        return GL_FALSE;
    }
    esRegisterDrawFunc(myesContext, Draw);
    esRegisterUpdateFunc(myesContext, Update);
    esRegisterShutdownFunc(myesContext, ShutDown);

    return GL_TRUE;
}

效果图

image

posted @ 2021-06-13 22:50  pyjetson  阅读(222)  评论(0编辑  收藏  举报