最简单的粒子特效(Transform Feedback ) 的关键代码示例【OpenGL】
2017-01-10 21:29
1351 查看
使用 Transform Feedback 的优点:
1.允许顶点着色器的输出存储在一个 buffer 对象上,之后其他 shader 程序可以读取 buffer 对象的数据进行绘制(本shader也可以读取它作为下一次顶点着色器的输入);2.允许在GPU(顶点着色器)上实现粒子发射器,不用依赖CPU,即不用在CPU上利用随机数初始化粒子,而把该操作迁移至顶点着色器中。
最终效果图
从上图可以看出,粒子不是同时发射的,而且运动路径并不受限与起点和终点的线性插值
关键代码和注释:
void InitEmitParticles()
{
std::string vShaderStr = TextFileRead("emit_vs.glsl");
std::string fShaderStr = TextFileRead("emit_fs.glsl");
userData->emitProgramObject = InitShaders(
vShaderStr.c_str(),
fShaderStr.c_str());
{
const char *feedbackVaryings[5] =
{
"v_position",
"v_velocity",
"v_size",
"v_curtime",
"v_lifetime"
};
// ###################################################
// Set the vertex shader outputs as transform feedback varyings
// ☆ 将顶点着色器的输出作为 transform feedback 的 varyings
// 即指定存入 transform feedback buffers 中的值
glTransformFeedbackVaryings(userData->emitProgramObject, 5, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
// ###################################################
// Link program must occur after calling glTransformFeedbackVaryings
// 必须在 glTransformFeedbackVaryings 之后
glLinkProgram(userData->emitProgramObject);
// Get the uniform locations - this also needs to happen after glLinkProgram is called again so
// that the uniforms that output to varyings are active
userData->emitTimeLoc = glGetUniformLocation(userData->emitProgramObject, "u_time");
userData->emitEmissionRateLoc = glGetUniformLocation(userData->emitProgramObject, "u_emissionRate");
userData->emitNoiseSamplerLoc = glGetUniformLocation(userData->emitProgramObject, "s_noiseTex");
}
}
void EmitParticles(float deltaTime) { // ☆ GLuint srcVBO = userData->particleVBOs[userData->curSrcIndex]; GLuint dstVBO = userData->particleVBOs[(userData->curSrcIndex + 1) % 2]; glUseProgram(userData->emitProgramObject); // ☆ 传入 srcVBO 保存的 varyings SetupVertexAttributes(srcVBO); // ################################################### // Set transform feedback buffer // glBindBuffer ( GL_TRANSFORM_FEEDBACK_BUFFER, dstVBO ); // 绑定一个 buffer object 到一个索引的 buffer target 上 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, dstVBO); // ################################################### // Turn off rasterization - we are not drawing // 关闭光栅化,不再绘制屏幕 glEnable(GL_RASTERIZER_DISCARD); // Set uniforms glUniform1f(userData->emitTimeLoc, userData->time); glUniform1f(userData->emitEmissionRateLoc, EMISSION_RATE); // Bind the 3D noise texture // 绑定三维噪声纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D, userData->noiseTextureId); glUniform1i(userData->emitNoiseSamplerLoc, 0); // ################################################### // Emit particles using transform feedback // 指定图元类型 glBeginTransformFeedback(GL_POINTS); // 保存变换后的 varyings 至 dstVBO 中 glDrawArrays(GL_POINTS, 0, NUM_PARTICLES); glEndTransformFeedback(); // ################################################### #ifdef BLOCK // Create a sync object to ensure transform feedback results are completed before the draw that uses them. // 确保绘制命令在使用它们时,transform feedback 的结果已经完成 // Create a new sync object and insert it into the GL command stream userData->emitSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); #endif // Restore state // 恢复初始状态 glDisable(GL_RASTERIZER_DISCARD); glUseProgram(0); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); // glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_3D, 0); // Ping pong the buffers // 交换 buffers(将 dstVBO 用于屏幕绘制和下一次的 srcVBO) userData->curSrcIndex = (userData->curSrcIndex + 1) % 2; }
void Draw() { // ----------------------------- #ifdef BLOCK // Block the GL server until transform feedback results are completed // 阻塞 GL server,直到 transform feedback 完成(sync object 接到通知) glWaitSync(userData->emitSync, 0, GL_TIMEOUT_IGNORED); // 标记为删除,当结束阻塞时删除 glDeleteSync(userData->emitSync); #endif // ----------------------------- // Set the viewport glViewport(0, 0, SCREEN_W, SCREEN_H); // Clear the color buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Use the program object // 使用 绘制 shader glUseProgram(userData->drawProgramObject); // Load the VBO and vertex attributes // ☆ 注意是当前的 VBO(最新的 transform feedback 的结果) SetupVertexAttributes(userData->particleVBOs[userData->curSrcIndex]); // Set uniforms glUniform1f(userData->drawTimeLoc, userData->time); glUniform4f(userData->drawColorLoc, 1.0f, 1.0f, 1.0f, 1.0f); glUniform2f(userData->drawAccelerationLoc, 0.0f, ACCELERATION); // Blend particles glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE); //glBlendFunc(GL_ONE, GL_ONE); glDrawArrays(GL_POINTS, 0, NUM_PARTICLES); glutSwapBuffers(); }
其余代码和注释:
#include "stdafx.h"
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "Noise3D.hpp"
#include "Utils.hpp"
#include "UserData.h"
//#define BLOCK 1
#define SCREEN_W 640
#define SCREEN_H 320
#define EMISSION_RATE .2f
#define ACCELERATION -.5f
enum ATTR_ENUM
{
ATTRIBUTE_POSITION = 0,
ATTRIBUTE_VELOCITY = 1,
ATTRIBUTE_SIZE = 2,
ATTRIBUTE_CURTIME = 3,
ATTRIBUTE_LIFETIME = 4
};
UserData *userData = NULL;
// 初始化 Shader Program,返回 Program ID
GLuint InitShaders(const char *vs, const char *fs)
{
GLint vertCompiled, fragCompiled, linked;
// Shaders
GLint v = glCreateShader(GL_VERTEX_SHADER);
GLint f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v, 1, &vs, NULL);
glShaderSource(f, 1, &fs, NULL);
glCompileShader(v);
glGetShaderiv(v, GL_COMPILE_STATUS, &vertCompiled); // Debug
if (vertCompiled != GL_TRUE)
{
printf("Vertex Shader compied error! \n");
}
glCompileShader(f);
glGetShaderiv(f, GL_COMPILE_STATUS, &fragCompiled);
if (fragCompiled != GL_TRUE)
{
printf("Fragment Shader compied error! \n");
}
//Program:
GLuint p = glCreateProgram();
glAttachShader(p, v);
glAttachShader(p, f);
glLinkProgram(p);
glGetProgramiv(p, GL_LINK_STATUS, &linked); // Debug
if (linked != GL_TRUE)
{
printf("Program linked error! \n");
}
return p;
}
void InitEmitParticles()
{
...
}
///
// Initialize the shader and program object
//
int Init()
{
Particle particleData[NUM_PARTICLES];
int i;
// ☆
InitEmitParticles();
std::string vShaderStr = TextFileRead("draw_vs.glsl");
std::string fShaderStr = TextFileRead("draw_fs.glsl");
// Load the shaders and get a linked program object
userData->drawProgramObject = InitShaders(vShaderStr.c_str(), fShaderStr.c_str());
// Get the uniform locations
userData->drawTimeLoc = glGetUniformLocation(userData->drawProgramObject, "u_time");
userData->drawColorLoc = glGetUniformLocation(userData->drawProgramObject, "u_color");
userData->drawAccelerationLoc = glGetUniformLocation(userData->drawProgramObject, "u_acceleration");
userData->time = 0.0f;
userData->curSrcIndex = 0;
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// Create a 3D noise texture for random values
// 创建 3D 噪声纹理 (128*128)
userData->noiseTextureId = Create3DNoiseTexture(128, 50.0);
// Initialize particle data
for (i = 0; i < NUM_PARTICLES; i++)
{
Particle *particle = &particleData[i];
particle->position[0] = 0.0f;
particle->position[1] = 0.0f;
particle->velocity[0] = 0.0f;
particle->velocity[1] = 0.0f;
particle->size = 0.0f;
particle->curtime = 0.0f;
particle->lifetime = 0.0f;
}
// Create the particle VBOs
glGenBuffers(2, &userData->particleVBOs[0]);
for (i = 0; i < 2; i++)
{
glBindBuffer(GL_ARRAY_BUFFER, userData->particleVBOs[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Particle) * NUM_PARTICLES, particleData, GL_DYNAMIC_COPY); // ❤ 意味着该buffer会被频繁写入
}
return TRUE;
}
void SetupVertexAttributes(GLuint vboID)
{
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glVertexAttribPointer(ATTRIBUTE_POSITION, 2, GL_FLOAT,
GL_FALSE, sizeof(Particle),
(const void *)NULL);
glVertexAttribPointer(ATTRIBUTE_VELOCITY, 2, GL_FLOAT,
GL_FALSE, sizeof(Particle),
(const void *)offsetof(Particle, velocity[0]));
glVertexAttribPointer(ATTRIBUTE_SIZE, 1, GL_FLOAT,
GL_FALSE, sizeof(Particle),
(const void *)offsetof(Particle, size));
glVertexAttribPointer(ATTRIBUTE_CURTIME, 1, GL_FLOAT,
GL_FALSE, sizeof(Particle),
(const void *)offsetof(Particle, curtime));
glVertexAttribPointer(ATTRIBUTE_LIFETIME, 1, GL_FLOAT,
GL_FALSE, sizeof(Particle),
(const void *)offsetof(Particle, lifetime));
glEnableVertexAttribArray(ATTRIBUTE_POSITION);
glEnableVertexAttribArray(ATTRIBUTE_VELOCITY);
glEnableVertexAttribArray(ATTRIBUTE_SIZE);
glEnableVertexAttribArray(ATTRIBUTE_CURTIME);
glEnableVertexAttribArray(ATTRIBUTE_LIFETIME);
}
void EmitParticles(float deltaTime)
{
...
}
///
// Update time-based variables
//
void Update(float deltaTime)
{
userData->time += deltaTime;
//printf("time = %lf\n", userData->time);
// ☆
EmitParticles(deltaTime);
}
///
// Draw a triangle using the shader pair created in Init()
//
void Draw()
{
...
}
///
// Cleanup
//
void ShutDown()
{
// Delete program object
glDeleteProgram(userData->drawProgramObject);
glDeleteProgram(userData->emitProgramObject);
glDeleteBuffers(2, &userData->particleVBOs[0]);
}
// 键盘响应事件
void ProcessNormalKeys(unsigned char key, int x, int y)
{
// Esc
if (key == 27)
{
ShutDown();
exit(0);
}
}
void Display(int value)
{
static float delta_time = 0.005f;
Update(delta_time);
Draw();
glutTimerFunc(value, &Display, value);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB /*| GLUT_STENCIL | GLUT_DEPTH*/);
glutInitWindowPosition(100, 100);
glutInitWindowSize(SCREEN_W, SCREEN_H);
glutCreateWindow("Hello Transform Feedback !");
GLenum err = glewInit();
if (err != GLEW_OK)
{
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
exit(-2);
}
userData = new UserData;
if (!Init())
{
return GL_FALSE;
}
glutDisplayFunc(Draw);
glutTimerFunc(10, &Display, 10);
glutKeyboardFunc(ProcessNormalKeys);
glutMainLoop();
return 0;
}
相关文章推荐
- openGL/ESv2 通过JNI的简单示例代码
- vue 粒子特效的示例代码
- 用opengl编写一个简单的画图软件示例代码
- 最简单的 MRTs(Multi Render Targets)的完整代码示例【OpenGL】
- 实现MSN弹出窗口特效示例代码
- Asp.net 2.0 一个简单的联动DropDownList示例(示例代码下载)
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- C#实现数据库事务处理的简单示例代码
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- 简单示例AJAX结合PHP代码实现登录效果代码
- Asp.net 2.0 一个简单的联动DropDownList示例(示例代码下载)
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- [导入]AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- [导入]Asp.net 2.0 一个简单的联动DropDownList示例(示例代码下载)
- Unity 简单示例代码和向导/Unity Aplication Block
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- Asp.net 2.0 一个简单的联动DropDownList示例(示例代码下载)
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)
- [导入]AjaxPanel自定义控件实现页面无刷新数据交互(做了个示例程序, 效果确实比较Cool, 用法非常简单! )(示例代码下载)