先前写了一个没有用到transformfeedback的粒子发射器,这次进行补全。
使用transformfeedback的好处:可以实时的对粒子进行控制,比如检测粒子与物体的碰撞。
更新后的粒子发射器类:
#ifndef PARTICLE_GENERATOR_H
#define PARTICLE_GENERATOR_H
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <GL\Shader.h>
using namespace glm;
using namespace std;
struct Particle
{
vec3 Position, Velocity;
vec4 Color;
GLfloat Life;
Particle() : Position(0.0f), Velocity(0.0f), Color(1.0f), Life(0.0f) { }
};
class ParticleGenerator
{
public:
ParticleGenerator(Shader shader, GLuint texture, GLuint amount, vec3 gravity, GLfloat scale);
void Update(GLfloat dt, vec3 position, vec3 velocity, GLuint newParticles, vec3 offset);
void Draw(mat4 projection, mat4 view, mat4 model, GLuint feedback,GLuint texbuffer);
private:
vector<Particle> particles;
GLuint amount;
Shader shader;
GLuint texture;
vec3 acceleration;
GLfloat scales;//改变粒子大小
GLboolean zhuangji;//判断粒子是否发生撞击
GLuint VAO;
void init();
GLuint firstUnusedParticle();
void respawnParticle(Particle &particle, vec3 position,vec3 velocity, vec3 offset = vec3(0.0f, 0.0f, 0.0f));
};
#endif
粒子发射器类中各个函数的具体实现:
1、构造函数
ParticleGenerator::ParticleGenerator(Shader shader, GLuint texture, GLuint amount, vec3 gravity, GLfloat scale): shader(shader), texture(texture), amount(amount), acceleration(gravity)
{
this->init();
this->scales = scale;
this->zhuangji = GL_FALSE;
}
2、update函数:将计算粒子速度的代码和计算粒子位置的代码删除,并交由shader进行计算
if (!this->zhuangji)
{
for (GLuint i = 0; i < newParticles; ++i)
{
int unusedParticle = this->firstUnusedParticle();
if (unusedParticle != -1)
{
this->respawnParticle(this->particles[unusedParticle], position, velocity, offset);
}
}
}
for (GLuint i = 0; i < this->amount; ++i)
{
Particle &p = this->particles[i];
p.Life -= 0.01f;
if (p.Life > 0.0f)
{
p.Color.a = p.Life;
}
}
3、draw函数
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
this->shader.Use();
GLuint total = 0;
for (vector<Particle>::iterator it = this->particles.begin(); it != this->particles.end(); it++)
{
if (it->Life > 0.0f)
{
glUniform1i(glGetUniformLocation(this->shader.Program, "triangle_count"), 12);
glUniform1f(glGetUniformLocation(this->shader.Program, "scales"), this->scales);
glUniform4f(glGetUniformLocation(this->shader.Program, "color"), it->Color.x, it->Color.y, it->Color.z, it->Color.w);
glUniform3f(glGetUniformLocation(this->shader.Program, "velocity"), it->Velocity.x, it->Velocity.y, it->Velocity.z);
glUniform3f(glGetUniformLocation(this->shader.Program, "lastposition"), it->Position.x, it->Position.y, it->Position.z);
glUniform3f(glGetUniformLocation(this->shader.Program, "acceleration"), this->acceleration.x, this->acceleration.y, this->acceleration.z);
glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "view"), 1, GL_FALSE, value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "model"), 1, GL_FALSE, value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(this->shader.Program, "projection"), 1, GL_FALSE, value_ptr(projection));
glBindTexture(GL_TEXTURE_BUFFER, texbuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->texture);
//使用transformfeedback获取来自着色器的变量中的数据
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, feedback, total * 7 * sizeof(GLfloat), 21 * sizeof(GLfloat));//为什么是21的理由:因为绘制的是GL_TRIANGLES,有三个顶点要绘制,需要把这三个顶点的信息都保存下来(虽然我们只需要一个定点的信息就行)
glBindVertexArray(this->VAO);
glBeginTransformFeedback(GL_TRIANGLES);
//glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_TRIANGLES, 0, 36);
//glDrawArrays(GL_POINTS, 0, 36);
glEndTransformFeedback();
glBindVertexArray(0);
total++;
}
}
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, feedback);
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, total * 7 * sizeof(GLfloat), datas);//将缓存对象中的数据读取到一个数组当中
int nums = 0;
//更新粒子的位置数据和速度数据
for (vector<Particle>::iterator it = this->particles.begin(); it != this->particles.end(); it++)
{
if (it->Life > 0.0f)
{
it->Position.x = datas[nums * 7];
it->Position.y = datas[nums * 7 + 1];
it->Position.z = datas[nums * 7 + 2];
it->Velocity.x = datas[nums * 7 + 3];
it->Velocity.y = datas[nums * 7 + 4];
it->Velocity.z = datas[nums * 7 + 5];
if (datas[nums * 7 + 6] == 1.0f)
{
this->zhuangji = GL_TRUE;
//cout << it->Position.x << " " << it->Position.y << " " << it->Position.z << " " << it->Velocity.x << " " << it->Velocity.y << " " << it->Velocity.z << endl;
}
nums++;
}
}
glUseProgram(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4、着色器代码
#version 330 core
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoords;
out vec2 TexCoords;
out vec4 ParticleColor;
out float zhuangji;
out vec3 position_out;
out vec3 velocity_out;
uniform mat4 view;
uniform mat4 model;
uniform mat4 projection;
uniform vec4 color;
uniform vec3 velocity;
uniform vec3 lastposition;
uniform float scales;
uniform vec3 acceleration;
uniform int triangle_count;
uniform samplerBuffer cubeBuffer;
float time_step = 0.05f;
bool intersect(vec3 orign,vec3 direction,vec3 v0,vec3 v1,vec3 v2,out vec3 point)
{
vec3 u, v, n;
vec3 w0, w;
float r, a, b;
u = (v1 - v0);
v = (v2 - v0);
n = cross(u, v);
w0 = orign - v0;
a = -dot(n, w0);
b = dot(n, direction);
r = a / b;
if (r < 0.0 || r > 1.0)
{
return false;
}
point = orign + r * direction;
float uu, uv, vv, wu, wv, D;
uu = dot(u, u);
uv = dot(u, v);
vv = dot(v, v);
w = point - v0;
wu = dot(w, u);
wv = dot(w, v);
D = uv * uv - uu * vv;
float s, t;
s = (uv * wv - vv * wu) / D;
if (s < 0.0 || s > 1.0)
{
return false;
}
t = (uv * wu - uu * wv) / D;
if (t < 0.0 || (s + t) > 1.0)
return false;
return true;
}
vec3 reflect_vector (vec3 v, vec3 n)
{
return v - 2.0 * dot(v, n) * n;
}
void main()
{
zhuangji = 0.0f;
vec3 new_velocity = velocity + acceleration * time_step;//速度速率
vec3 new_position = lastposition + new_velocity * time_step;//位置
vec3 v0, v1, v2;
vec3 point;
ParticleColor = color;
TexCoords = texcoords;
for (int i = 0; i < triangle_count; i++)
{
v0 = texelFetch(cubeBuffer, i * 3).xyz;
v1 = texelFetch(cubeBuffer, i * 3 + 1).xyz;
v2 = texelFetch(cubeBuffer, i * 3 + 2).xyz;
if (intersect(lastposition.xyz, lastposition.xyz - new_position.xyz, v0, v1, v2, point))
{
vec3 n = normalize(cross(v1 - v0, v2 - v0));
new_position = point + reflect_vector(new_position.xyz - point, n);
vec3 xx = reflect_vector(new_velocity, n);
new_velocity = 0.8 * vec3(xx.x * 5.0f, xx.y/3.0f, xx.z * 5.0f);
zhuangji = 1.0f;
}
}
velocity_out = new_velocity;
position_out = new_position;
gl_Position = projection * view * model * vec4(Position * scales + lastposition, 1.0f);
}
5、主程序更改
Shader cubeshader("ball.vs", "ball.frag");
GLchar* cubevaryings[] = { "world_space_position" };
glTransformFeedbackVaryings(cubeshader.Program, 1, cubevaryings, GL_INTERLEAVED_ATTRIBS);
glLinkProgram(cubeshader.Program);
glUseProgram(cubeshader.Program);
glUniform1i(glGetUniformLocation(cubeshader.Program, "diffuseMap"), 0);
glUseProgram(0);
Shader particleshader("particles.vs", "particles.frag");
GLchar* particlevaryings[] = {"position_out","velocity_out","zhuangji"};
glTransformFeedbackVaryings(particleshader.Program, 3, particlevaryings, GL_INTERLEAVED_ATTRIBS);
glLinkProgram(particleshader.Program);
glUseProgram(particleshader.Program);
glUniform1i(glGetUniformLocation(particleshader.Program, "sprite"), 0);
glUseProgram(0);
GLuint cubefeedback, cubeTEX;
glUseProgram(cubeshader.Program);
glGenBuffers(1, &cubefeedback);
glBindBuffer(GL_TEXTURE_BUFFER, cubefeedback);
glBufferData(GL_TEXTURE_BUFFER, 160 * sizeof(GLfloat), NULL, GL_DYNAMIC_COPY);
glGenTextures(1, &cubeTEX);
glBindTexture(GL_TEXTURE_BUFFER, cubeTEX);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, cubefeedback);
glUseProgram(0);
GLuint particlefeedback;
glGenBuffers(1, &particlefeedback);
glBindBuffer(GL_ARRAY_BUFFER, particlefeedback);
glBufferData(GL_ARRAY_BUFFER, 5000 * 36 * 6 * sizeof(GLfloat), NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
while (!glfwWindowShouldClose(window))
{
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glfwPollEvents();
Do_Movement();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mat4 projection = perspective(radians(camera.Zoom), (GLfloat)screenWidth / (GLfloat)screenHeight, 0.1f, 100.0f);
mat4 model = mat4();
model = translate(model, vec3(0.0f, -2.5f, 0.0f));
mat4 view = camera.GetViewMatrix();
cubeshader.Use();
glUniformMatrix4fv(glGetUniformLocation(cubeshader.Program, "model"), 1, GL_FALSE, value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(cubeshader.Program, "view"), 1, GL_FALSE, value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(cubeshader.Program, "projection"), 1, GL_FALSE, value_ptr(projection));
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, cubefeedback);
glBeginTransformFeedback(GL_TRIANGLES);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, woodtexture);
RenderCube();
glEndTransformFeedback();
glUseProgram(0);
model = mat4();
//model = scale(model, vec3(0.1f, 0.1f, 0.1f));//bug~~~~~~~~~~~~~~~~~!!!!!!!!!!!!逻辑错误!!!!!!
particle->Draw(projection, view, model, particlefeedback, cubeTEX);
particle->Update(deltaTime, vec3(0.0f, 100.0f - currentFrame * 10.0f, 0.0f), vec3(0.0f, -150.0f, 0.0f), 15, vec3(0.0f));
glfwSwapBuffers(window);
}
效果: