1. 程式人生 > 其它 >【openGLES3.0程式設計指南筆記-11】粒子系統

【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;
}

效果圖