概述
粒子的属性:
寿命 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;
}