asp网站作业下载,上海外贸,想创建一个网站,绥化网站建设公司一、简介
本文介绍了如何使用 OpenGL 中的 Transform Feedback 实现粒子效果#xff0c;最终可以实现下图的效果#xff1a; 本文的粒子系统实现参考了modern-opengl-tutorial, ogldev-tutorial28 和 粒子系统–喷泉 [OpenGL-Transformfeedback]。
二、使用 TransformFeed…一、简介
本文介绍了如何使用 OpenGL 中的 Transform Feedback 实现粒子效果最终可以实现下图的效果 本文的粒子系统实现参考了modern-opengl-tutorial, ogldev-tutorial28 和 粒子系统–喷泉 [OpenGL-Transformfeedback]。
二、使用 TransformFeedback 实现效果
1. Transform Feedback 简介
Transform Feedback 是 OpenGL 中用于获取 vertex shader 和 geometry shader 处理后的顶点数据的一种机制可以在 GPU 上将 vertex shader, geometry shader 处理后的数据存储到以一个 buffer 中而不进行接下来的 clipper, Rasterizer 和 Fragment Shader 阶段。 Transform Feedback Buffer 在渲染管线中所处的位置如下图所示
基于 Transform feedback我们可以在 GPU 上对多个顶点数据行进并行运算粒子系统 就是 Transform feedback 的一个典型应用。
2. 粒子系统实现
在实现粒子系统时使用 update Shader 和 render Shader 两个 着色器
update shader 用来更新粒子的状态包括更新粒子状态、生成新粒子、消灭旧粒子。render shader 用来将粒子显示在屏幕上。
粒子系统的实现流程如下 上图展示了使用 Update shader 和 Render shader 实现粒子系统的流程。图中左侧黄色虚线内为使用 Update shader 更新粒子右侧蓝色虚线内为使用 Render shader 将粒子渲染到屏幕上然后再进入下一帧的Update-Render流程。 在 Update shader 中输入为 Update input VBO输出为 Update output VBO。在 Render shader 中Update output VBO 又作为渲染时的输入Render input VBO。由于 Transform Feedback 中的在读 一个 VBO 时不能同时写该 VBO 及Update input VBO 与 Update output VBO 不能是同一个 buffer object。因此在代码实现使用两个 VBO 交替作为 一个Update-Render流程中的Update input VBO 和 Update output VBO。 例如渲染一个n帧的结果其 Update input VBO 和 Update output VBO 所代表的 buffer 变换如下所示
3. 部分代码讲解
3.1. Particle 类
struct Particle
{float Type; // 0: launch, 1: shell, 2 : second shellglm::vec3 Pos;glm::vec3 Velocity;float Life;
};系统中粒子的类型分为三类 launch, shell 和 second shell。
launch 类粒子相当于一个发射器其位置、速度一直保持不变在 Life 到达一定的数值时生成 shell 类粒子shell 类粒子由 launch 类粒子生成后获得一个初始的速度假设只受到重力根据牛顿第二定律更新自己的 速度、位置。并且 shell 粒子的 Life 在到达一定数值时生成 second shell 类粒子second shell 类粒子初始时于生成该粒子的父粒子具有相同的位置但是速度不同。 second shell 粒子的 Life 到达一定数值后死亡。
3.2. PaticleSystem 类
a. PaticleSystem类的变量
class ParticleSystem
{
public:
...
private:bool m_isFirst; // 标记 是否时第一次调用 Render()GLuint m_VAO[2]; // 两个 VAO 分别用于 update 和 render 的输入unsigned int m_update_input_id; // update input id,unsigned int m_render_input_id; // render input id, update output idGLuint m_VBO_TFB[2]; // 两个顶点缓冲区 , 交替作为 update / render bufferGLuint m_TFO[2]; // 两个 transform feedback 对象 TFOShader m_updateShader; // particle update shaderShader m_renderShader; // particle render shaderTexture m_randomTexture; // 随机数纹理Texture m_particleTexture; // 粒子的纹理float m_time; // 系统运行的总时间...
}b. InitParticleSystem() 初始化 ParticleSystem
class ParticleSystem
{public:...bool InitParticleSystem(const glm::vec3 Pos){// 1. 生成 初始粒子Particle Particles[MAX_PARTICLES];Particles[0].Type 0;Particles[0].Pos Pos;Particles[0].Velocity glm::vec3(0.0f, 0.01f, 0.0f);Particles[0].Life 0.0f;// 2. 初始化 VAO, TFO, VBOglGenVertexArrays(2, m_VAO); // 生成 两个 VAOglGenTransformFeedbacks(2, m_TFO); // 生成 两个 TFOglGenBuffers(2, m_VBO_TFB); // 生成 两个 buffer (TFB), 分别绑定到 对应的 VAO 和 TFO 上for (unsigned int i 0; i 2; i){// VAO[i] - VBO[i]// TFO[i] - VBO[i]glBindVertexArray(m_VAO[i]);glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TFO[i]);glBindBuffer(GL_ARRAY_BUFFER, m_VBO_TFB[i]);glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_VBO_TFB[i]);glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(Particles), Particles, GL_DYNAMIC_DRAW);glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_VBO_TFB[i]);}// 3. 初始化 update shader, render shader// update shaderconst char *feedbackVaryings[] {Type1, Position1, Velocity1, Age1};m_updateShader Shader(../resources/particleUpdate.vert, ../resources/particleUpdate.frag,../resources/particleUpdate.geom, feedbackVaryings);m_updateShader.use();m_updateShader.setFloat(gLauncherLifetime, 100.0f);m_updateShader.setFloat(gShellLifetime, 10000.0f);m_updateShader.setFloat(gSecondaryShellLifetime, 500.f);// 初始化 render shaderm_renderShader Shader(../resources/particleRender.vert, ../resources/particleRender.frag,../resources/particleRender.geom);m_renderShader.use();m_renderShader.setFloat(gBillboardSize, 0.01f);// 4. 初始化 纹理// 随机数纹理m_randomTexture.id TextureFromRand();m_randomTexture.path random;m_randomTexture.type texture_diffuse;// 粒子纹理m_particleTexture.id TextureFromFile(particle.png, ../resources/textures/);m_particleTexture.path ../resources/textures/particle.png;m_particleTexture.type texture_diffuse;return true;};...
}c. Render() 调用 update shader 和 Render shader 进行更新粒子、渲染粒子
class ParticleSystem
{
public:
...void Render(float DeltaTimeMillis, const glm::mat4 VP, const glm::vec3 CameraPos){m_time DeltaTimeMillis;// 更新 粒子updateParticles(DeltaTimeMillis);// 渲染 粒子renderParticles(VP, CameraPos);// 交换 update shader 使用的 VAO 和 TFO// 0 - 1 - 0 - 1 - 0 - ...m_update_input_id (m_update_input_id 1) % 2;// 交换 render shader 使用的 VAO// 1 - 0 - 1 - 0 - 1 - ...m_render_input_id (m_render_input_id 1) % 2;};...
}d. updateParticles() 更新粒子
class ParticleSystem
{
public:
...void updateParticles(float DelatTimeMillis){// 1. 设置 update shader 中的 uniform 变量以及纹理变量m_updateShader.use();m_updateShader.setFloat(gTime, m_time);m_updateShader.setFloat(gDeltaTimeMillis, 1.0f * DelatTimeMillis);glActiveTexture(GL_TEXTURE0); // 激活纹理单元 0glUniform1i(glGetUniformLocation(m_updateShader.ID, gRandomTexture),0); // 将纹理单元0 与着色器的 sampler 变量 gRandomTexture 关联glBindTexture(GL_TEXTURE_1D, m_randomTexture.id); // 将 纹理对象 绑定到当前的纹理单元 GL_SAMPLER_1D 纹理上// 2. 绑定 VAO, TFB// 绑定VAO, 作为 update shader 的输入glBindVertexArray(m_VAO[m_update_input_id]);// 根据 update shader 设置 VAO 中不同属性的读取方式glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(Particle), 0); // typeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), (const GLvoid *)(sizeof(float))); // positionglVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Particle),(const GLvoid *)(sizeof(float) sizeof(glm::vec3))); // velocityglVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(Particle),(const GLvoid *)((sizeof(float) sizeof(glm::vec3)) sizeof(glm::vec3))); // lifetimeglEnableVertexAttribArray(0);glEnableVertexAttribArray(1);glEnableVertexAttribArray(2);glEnableVertexAttribArray(3);// 绑定 TFO, 作为 update shader 的输出glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TFO[m_render_input_id]);// 3. 开始使用 update shader 更新粒子glEnable(GL_RASTERIZER_DISCARD); // 跳过光栅化以及之后的阶段glBeginTransformFeedback(GL_POINTS);if (m_isFirst){ // 第一次 运行 update shader, 只有一个 粒子glDrawArrays(GL_POINTS, 0, 1);m_isFirst false;}else{ // 之后运行 update shader, 粒子个数不确定, 由 opengl 根据 transform feedback object 自行确定粒子个数glDrawTransformFeedback(GL_POINTS, m_TFO[m_update_input_id]);}glEndTransformFeedback();glDisable(GL_RASTERIZER_DISCARD); // 开启光栅化以及之后的阶段glDisableVertexAttribArray(0);glDisableVertexAttribArray(1);glDisableVertexAttribArray(2);glDisableVertexAttribArray(3);};...
}e. renderParticles()渲染粒子
class ParticleSystem
{
public:
...void renderParticles(const glm::mat4 VP, const glm::vec3 CameraPos){// 1. 设置渲染状态glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT); // 使用 (0.2,0.3,0.3,1.0) 清空 color texture, 清空 depth bufferglEnable(GL_BLEND); // 启用 blendglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blend 模式为 D alpha*S (1-alpha)*DglEnable(GL_PROGRAM_POINT_SIZE);// 2. 设置 render shader 中的 uniform 变量以及纹理变量m_renderShader.use();m_renderShader.setVec3(gCameraPos, CameraPos);m_renderShader.setMat4(gVP, VP);glActiveTexture(GL_TEXTURE1); // 激活纹理单元 1glUniform1i(glGetUniformLocation(m_renderShader.ID, gColorMap), 1);glBindTexture(GL_TEXTURE_2D, m_particleTexture.id); // 将 纹理对象 绑定到当前的纹理单元的 GL_SAMPLER_1D 纹理上// 3. 绑定 VAO// 绑定VAO, 作为 render shader 的输入glBindVertexArray(m_VAO[m_render_input_id]);// 根据 render shader 设置 VAO 中不同属性的读取方式glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), (void *)(sizeof(float))); // positionglEnableVertexAttribArray(0);// 4. 开始使用 render shader 渲染粒子glDisable(GL_RASTERIZER_DISCARD); // 开启 光栅化 以及之后的阶段glDrawTransformFeedback(GL_POINTS, m_TFO[m_render_input_id]);};...
}3.3. Update shader
a. Vertex shader
#version 410layout(location 0) in float Type;
layout(location 1) in vec3 Position;
layout(location 2) in vec3 Velocity;
layout(location 3) in float Age;out float Type0;
out vec3 Position0;
out vec3 Velocity0;
out float Age0;void main() {Type0 Type;Position0 Position;Velocity0 Velocity;Age0 Age;
}b. Geometer shader
#version 410layout(points) in;
layout(points, max_vertices 30) out;/* 从 vertex shader 输入的 point 的属性 */
in float Type0[];
in vec3 Position0[];
in vec3 Velocity0[];
in float Age0[];/* 输出到 fragment shader 的 point 的属性*/
out float Type1;
out vec3 Position1;
out vec3 Velocity1;
out float Age1;/* 用于更新 particle 的变量 */
uniform float gDeltaTimeMillis; // 时间间隔
uniform float gTime; // 当前时刻
uniform sampler1D gRandomTexture; // 随机纹理
uniform float gLauncherLifetime; // Launcher 的生存时间
uniform float gShellLifetime; // Shell 的生存时间
uniform float gSecondaryShellLifetime; // Secondary Shell 的生存时间#define PARTICLE_TYPE_LAUNCHER 0.0f
#define PARTICLE_TYPE_SHELL 1.0f
#define PARTICLE_TYPE_SECONDARY_SHELL 2.0f// 使用 random texture 获取一个随机值 (random texture相当于一个随机数池)
vec3 GetRandomDir(float TexCoord) {vec3 Dir texture(gRandomTexture, TexCoord).xyz;Dir - vec3(0.5, 0.5, 0.5);return Dir;
}void main() {// 更新 particle 的生存时间float Age Age0[0] gDeltaTimeMillis;// 增加随机性float g_Time (sin(gTime) 1.0) / 2.0 * 1000.0;g_Time gTime;// Launcher particleif (Type0[0] PARTICLE_TYPE_LAUNCHER) {// 如果 particle 生存时间过长// 那么就生成一个 Shell particle, 并且更新 Launcher particleif (Age gLauncherLifetime) {// 生成 一个 Shell particleType1 PARTICLE_TYPE_SHELL;// 初始化 position, dir, velocity, agePosition1 Position0[0];vec3 Dir GetRandomDir(g_Time / 1000.0);Dir.y max(Dir.y, 0.95);Velocity1 normalize(Dir) / 12.0;// Velocity1 Velocity0[0];Age1 0.0;// emit vertexEmitVertex();EndPrimitive();Age 0.0;}// 更新 Launcher particleType1 PARTICLE_TYPE_LAUNCHER;Position1 Position0[0];Velocity1 Velocity0[0];Age1 Age;EmitVertex();EndPrimitive();} else {// 如果是 Shell or Second Shell particlefloat DeltaTimeSecs gDeltaTimeMillis / 1000.0;float t1 Age0[0] / 1000.0;float t2 Age / 1000.0;// position 的改变量vec3 DeltaP DeltaTimeSecs * Velocity0[0];// velocity 的改变量// vec3 DeltaV vec3(DeltaTimeSecs) * vec3(0.0, -9.81, 0.0);// 如果是 Shell particlevec3 DeltaV vec3(0, DeltaTimeSecs / 1000.0 * -9.81, 0);if (Type0[0] PARTICLE_TYPE_SHELL) {if (Age gShellLifetime) {// 如果 Shell particle 还在生存时间内Type1 PARTICLE_TYPE_SHELL;// 更新 position, velocityPosition1 Position0[0] DeltaP;Velocity1 Velocity0[0] DeltaV;// Velocity1 Velocity0[0];// Velocity1 Velocity0[0] vec3(0.0, DeltaTimeSecs * -9.8, 0.0);Age1 Age;EmitVertex();EndPrimitive();} else {// 如果 Shell particle 超过生存时间了那么就 分裂为 10 个 Second Shellfor (int i 0; i 10; i) {Type1 PARTICLE_TYPE_SECONDARY_SHELL;Position1 Position0[0];vec3 Dir GetRandomDir((g_Time i) / 1000.0);Velocity1 normalize(Dir) / 20.0;Age1 0.0f;EmitVertex();EndPrimitive();}}} else {// 如果是 Second Shell particleif (Age gSecondaryShellLifetime) {// 如果 Second Shell 还在生存周期内Type1 PARTICLE_TYPE_SECONDARY_SHELL;Position1 Position0[0] DeltaP;Velocity1 Velocity0[0] DeltaV;Age1 Age;EmitVertex();EndPrimitive();}// 如果 Second Shell 超过生存周期, 那么就消灭该 Second Shell particle// (什么也不做)}}
}c. Fragment shader
#version 410 core
void main() {// do nothing
}3.4. Render shader
a. Vertex shader
#version 410
layout(location 0) in vec3 Position;
void main() { gl_Position vec4(Position, 1.0); }b. Geometer shader
#version 410layout(points) in;
layout(triangle_strip, max_vertices 4) out;
uniform mat4 gVP;
uniform vec3 gCameraPos;
uniform float gBillboardSize;out vec2 TexCoord;void main() {// 以 p0 gl_Position 为右下角绘制一个矩形 (两个三角形)// p2 --- p4// | \ |// | \ |// p1 --- p3 (p0)vec3 Pos gl_in[0].gl_Position.xyz;vec3 toCamera normalize(gCameraPos - Pos);vec3 up vec3(0.0, 1.0, 0.0);vec3 right cross(toCamera, up) * gBillboardSize;// p1Pos - right;gl_Position gVP * vec4(Pos, 1.0);TexCoord vec2(0.0, 0.0);EmitVertex();// p2Pos.y gBillboardSize;gl_Position gVP * vec4(Pos, 1.0);TexCoord vec2(0.0, 1.0);EmitVertex();// p3Pos.y - gBillboardSize;Pos right;gl_Position gVP * vec4(Pos, 1.0);TexCoord vec2(1.0, 0.0);EmitVertex();// p4Pos.y gBillboardSize;gl_Position gVP * vec4(Pos, 1.0);TexCoord vec2(1.0, 1.0);EmitVertex();EndPrimitive();
}c. Fragment shader
#version 410uniform sampler2D gColorMap;
in vec2 TexCoord;
out vec4 FragColor;
void main() {FragColor texture(gColorMap, TexCoord);if (FragColor.r 0.9 FragColor.g 0.9 FragColor.b 0.9) {discard;}
}4. 全部代码及模型文件
用于实现粒子效果的全部代码以及模型文件可以在OpenGL使用TransformFeedback实现粒子效果 中下载。
三、参考引用
[1]. modern-opengl-tutorial [2]. ogldev-tutorial28 [3]. 粒子系统–喷泉 [OpenGL-Transformfeedback]