一、所谓的target到底是指什么?
在使用一些绑定函数的时候,例如:glBindBuffer、glBindBufferBase,这些函数的第一个参数是一般称之为target,其往往是:GL_ARRAY_BUFFER、GL_TRANSFORM_FEEDBACK_BUFFER、GL_TEXTURE_BUFFER,这些东西,那么所谓的target到底是什么呢?
如果我们查询glew.h里面的内容的话,会找到这么一句话:
#define GL_ARRAY_BUFFER 0x8892
显然,GL_ARRAY_BUFFER仅仅代表一个地址。
所以说,在opengl里,大部分opengl object被使用时必须绑定至一个称之为“target”的特定位置,不同类型的opengl object需要绑定至不同的target(也就是地址)。将一个object绑定至一个target后,不影响该object绑定至其他target,即一组数据我们可以重复利用。
例如:GL_ARRAY_BUFFER,它仅仅表示我们要将某些数据用于更新顶点属性。
那么,为什么要先介绍这个呢?那是因为,我学了opengl三个月,一直以为target是用来生命缓存对象的类型,并没有真正理解它的意思,导致我在学习transform feedback的时候受到一些阻挠。。。
二、TransformFeedback
transformfeedback是用于获取顶点着色器或者几何着色器输出的数据,它最经典应用是用于粒子系统。
下面这张图,解释了transformfeedback的工作流程。
所以我们要做的事情很简单:1、截取顶点或几何着色器的数据,同时进行粒子的绘制。2、对缓存进行数据更新,用于下次绘制粒子。
要注意的一点是,我们上面的流水线中,粒子数据是使用一个Buffer作为输入、另一个Buffer作为(Trasform Feedback的)输出的,因为一个Buffer不可能同时作为同一批次流水线数据的输入和输出(考虑流水线顶点数据的并行处理)。于是我们的Input Buffer和Output Buffer是两个不同的Buffer,要完成上面那个循环,就得不断交换这两个Buffer作为“输入“和”输出“的角色。
初始化:
Shader particleshader("particle.vs", "particle.frag");
GLchar* feedbackVaryings[] = { "position_out", "velocity_out" };
glTransformFeedbackVaryings(particleshader.Program, 2, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
//该函数用于指明需要获取shader中的那些变量的数据
glLinkProgram(particleshader.Program);
glUseProgram(particleshader.Program);
GLuint updateVAO[2];
glGenVertexArrays(2, updateVAO);
GLuint updateVBO[2];
glGenBuffers(2, updateVBO);
for (int i = 0; i < 2; i++)
{
glBindVertexArray(updateVAO[i]);
glBindBuffer(GL_ARRAY_BUFFER, updateVBO[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(particles), particles, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
}
glUseProgram(0);
glBindVertexArray(0);
GLuint particleTBO[2];
glGenBuffers(2, particleTBO);
for (int i = 0; i < 2; i++)
{
glBindBuffer(GL_ARRAY_BUFFER, particleTBO[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(particles), NULL, GL_DYNAMIC_COPY);
}
glBindBuffer(GL_ARRAY_BUFFER,0);
获取顶点着色器或几何着色器数据的代码:
glUseProgram(particleshader.Program);
glUniformMatrix4fv(glGetUniformLocation(particleshader.Program, "model"), 1, GL_FALSE, value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(particleshader.Program, "view"), 1, GL_FALSE, value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(particleshader.Program, "projection"), 1, GL_FALSE, value_ptr(projection));
glUniform1i(glGetUniformLocation(particleshader.Program, "triangle_count"), 12);
glBindTexture(GL_TEXTURE_BUFFER, renderTEX);//这句话在后面会有解释这里可以先无视掉
if (count % 2 == 1)
{
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particleTBO[0]);//保存顶点着色器传出的数据
glBindVertexArray(updateVAO[1]);//传入原先的数据
}
else
{
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particleTBO[1]);
glBindVertexArray(updateVAO[0]);
}
//glEnable(GL_RASTERIZER_DISCARD);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, point_count);
glEndTransformFeedback();
//glDisable(GL_RASTERIZER_DISCARD);
//这里需要说明一下,在某些博客中我见到的代码往往进行了两次渲染,第一次仅仅是获取了数据,使用了glEnable(GL_RASTERIZER_DISCARD);来关闭光栅化,使其不会经过片断着色器。然后第二次渲染的时候用更新后的数据进行渲染。。。。。我觉得有点多余,就直接在获取数据的同时进行渲染,然后更新数据。。。我不知道这样写会不会有什么问题,希望有大牛能告诉我一下。。。。
更新数据:
if (count % 2 == 1)
{
glBindVertexArray(updateVAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, particleTBO[0]);
//这里可以看出,particleTBO[0]这个缓存的数据绑定至多个target,如果此时你用glGetBufferSubData分别获取GL_ARRAY_BUFFER和GL_TRANSFORM_FEEDBACK_BUFFER的数据,你会发现是一样的。
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
}
else
{
glBindVertexArray(updateVAO[1]);
glBindBuffer(GL_ARRAY_BUFFER, particleTBO[1]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
}
三、检测碰撞
首先我们先把要被粒子撞击的物体绘制出来
初始化:
GLuint renderVAO, renderTBO, renderTEX;
glUseProgram(particleshader.Program);
glGenBuffers(1, &renderTBO);
glBindBuffer(GL_TEXTURE_BUFFER, renderTBO);//该缓存用于记录绘制物体的三角面片数据
glBufferData(GL_TEXTURE_BUFFER, 160 * sizeof(GLfloat), NULL, GL_DYNAMIC_COPY);
glGenTextures(1, &renderTEX);//声明纹理,将上述缓存绑定到该纹理后可以传递到shader当中
glBindTexture(GL_TEXTURE_BUFFER, renderTEX);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, renderTBO);
glGenVertexArrays(1, &renderVAO);
glBindVertexArray(renderVAO);
glBindBuffer(GL_ARRAY_BUFFER, renderTBO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL);
glBindVertexArray(0);
glUseProgram(0);
绘制物体:
mat4 model = mat4();
mat4 view = camera.GetViewMatrix();
mat4 projection = perspective(radians(camera.Zoom), (GLfloat)screenWidth / (GLfloat)screenHeight, 0.1f, 100.0f);
glUseProgram(cubeshader.Program);
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));
glBindVertexArray(renderVAO);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, renderTBO);
glBeginTransformFeedback(GL_TRIANGLES);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, woodTexture);
RenderCube();
glEndTransformFeedback();
glUseProgram(0);
vertexshader:
#version 330 core
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoords;
out vec4 world_space_position;
out vec3 vs_fs_normal;
out vec2 TexCoords;
void main()
{
world_space_position = model * vec4(position, 1.0f);
vs_fs_normal = transpose(inverse(mat3(view * model))) * normal;
TexCoords = texcoords;
gl_Position = projection * view * model * vec4(position, 1.0f);
}
fragmentshader:
#version 330 core
uniform sampler2D diffuseMap;
out vec4 color;
in vec2 TexCoords;
in vec3 vs_fs_normal;
in vec4 world_space_position;
void main()
{
color = vec4(texture(diffuseMap, TexCoords).rgb, 1.0f);
}
接下来是检测碰撞的shader代码,这里我使用重心法来判断是否发生碰撞,推到如下:
代码如下:
#version 330 core
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform int triangle_count;
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 velocity;
out vec3 position_out;
out vec3 velocity_out;
float time_step = 0.02;
uniform samplerBuffer geometry_tbo;
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()
{
vec3 acceleration = vec3(0.0, -0.3, 0.0);//加速度
vec3 new_velocity = velocity + acceleration * time_step;//速度速率
vec3 new_position = position + new_velocity * time_step;//位置
vec3 v0, v1, v2;
vec3 point;
for (int i = 0; i < triangle_count; i++)
{
v0 = texelFetch(geometry_tbo, i * 3).xyz;
v1 = texelFetch(geometry_tbo, i * 3 + 1).xyz;
v2 = texelFetch(geometry_tbo, i * 3 + 2).xyz;
if (intersect(position.xyz, position.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);
new_velocity = 0.8 * reflect_vector(new_velocity, n);
}
}
if (new_position.y < -40.0)
{
new_position = vec3(-new_position.x * 0.3, position.y + 80.0, 0.0);
new_velocity *= vec3(0.2, 0.1, -0.3);
}
velocity_out = new_velocity * 0.9999;
position_out = new_position;
gl_Position = projection * view * model * vec4(position, 1.0f);
}
fragmentshader:
#version 330 core
out vec4 color;
in vec3 position_out;
in vec3 velocity_out;
void main()
{
color = vec4(0.9f, 0.9f, 0.9f, 1.0f);
}
很丑,不要吐槽