【openGLES3.0程式設計指南筆記-11】粒子系統
阿新 • • 發佈:2021-06-14
目錄
概述
粒子的屬性:
壽命 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, ¢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; }