在使用一些绑定函数的时候,例如: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的工作流程。
所以我们要做的事情很简单: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); }
很丑,不要吐槽