关于研究了两天的TransformFeedback的一些东西

本文介绍了OpenGL中粒子系统的实现方法,重点讲解了TransformFeedback的使用及如何通过顶点和几何着色器获取数据。此外,还详细阐述了如何利用重心法进行碰撞检测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、所谓的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);
}

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

很丑,不要吐槽

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值