粒子的属性:
寿命 a_lifetime
开始位置 a_startPosition
结束位置 a_endPosition
中心位置 u_centerPosition
颜色 u_color随机生成
时间 u_time持续一秒钟
1000个粒子,在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; }
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, ¢erPos[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, ¢erPos[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; }