TransformFeedback

变换反馈

将顶点数据发给着色器,变换后从着色器获取数据作为新的顶点数据进行绘制,可以用于绘制粒子及动画效果

一、shader.cpp文件

1
2
const GLchar* feedbackVaryings[] = { "outposition","fragcolor"};
glTransformFeedbackVaryings(ProgramID, 2, feedbackVaryings, GL_SEPARATE_ATTRIBS);

这两行代码需要放在glLinkProgram(ProgramID);之前。第一个数组说明了捕获到一个缓冲区中的输出属性(与顶点着色器中需要捕获的out的名字相同)。第二个函数的第一个参数是着色器程序;第二个参数和第三个参数指定了输出名称数组和数组本身的长度,最后一个参数指定了应该如何写入数据。以下两种格式可供选择:

1.–GL_INTERLEAVED_ATTRIBS:将所有属性写入一个缓冲区对象。

2.–GL_SEPARATE_ATTRIBS: 将属性写入多个缓冲区对象,或将不同的偏移量写入缓冲区。

比如一个程序有颜色和顶点两个缓冲区,使用第一个参数只能绑定一个缓冲区,此时想变换颜色和顶点需要把这两组数据写在同一个数组并设置读取的偏移量:

1
2
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databufferb);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databuffera);

使用第二个参数就可以将颜色数据和顶点数据分开,将两个缓冲区绑定到trasnformfeedback上:

1
2
3
4
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databufferb);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databuffera);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, colorbufferb);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, colorbuffera);

其中第二个参数就是缓冲区索引,绑定两个的话,第一个缓冲区索引是0,第二个是1,可以区分缓冲区。

二、顶点着色器文件

1
2
3
4
5
6
in vec4 inposition;
in vec4 color;
out vec4 outposition;
out vec4 fragcolor;//与feedbackVaryings[]同名
/*在main函数中对inposition和color进行变换*/
gl_Position=MVP * inposition;

输入的顶点属性数据inposition经过MVP变换后作为最终的gl_Position数据用来绘制图形;将inposition数据变换后存入outposition写入tf缓冲。(color同理)

三、应用程序文件

以GL_INTERLEAVED_ATTRIBS参数为例:

1
2
3
4
5
6
7
//tf缓冲区只用创建一个
GLuint tfbuffera;
glGenTransformFeedbacks(1, &tfbuffera);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfbuffera);
//给tf缓冲绑定相应的缓冲对象(databuffer用GL_ARRAY_BUFFER类型创建)
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databufferb);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databuffera);

相同的缓存不能同时作为输入和输出,所以如果想产生连续变换,需要两个缓冲区交换数据。所以需要两个databuffer。第一个buffer绑定data数据用于绘制第一个图形,第二个传递了一个nullptr,以创建一个足够大的缓冲区。(比如粒子系统生成的定点数会越来越多,此处不需要考虑该问题)

1
2
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), nullptr, GL_DYNAMIC_DRAW);

主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//绑定
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfbuffera);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, databuffer[flag]);//将变换后数据写入databufferb
glEnable(GL_RASTERIZER_DISCARD);//此次绘制写入tf缓冲因此不进行绘制,禁用光栅化
glBeginTransformFeedback(GL_TRIANGLES);//开启tf缓冲,设置绘制模式与draw相同
//用databuffera也就是最初的数据传入顶点着色器,数据进行第一次变换,但此时不显示,而是写入tf缓冲的databufferb
glEnableVertexAttribArray(positionID);
glBindBuffer(GL_ARRAY_BUFFER, databuffer[!flag]);
glVertexAttribPointer(positionID,4,GL_FLOAT,GL_FALSE,32,(void*)0);
glVertexAttribPointer(colorID,4,GL_FLOAT,GL_FALSE,32,(void*)16);
glDrawArrays(GL_TRIANGLES, 0, 36);
//结束转换反馈模式
glEndTransformFeedback();
//使用光栅化,绘制图形
glDisable(GL_RASTERIZER_DISCARD);
//使用databufferb第一次变换的数据绘制图形,因为此使没开启tf缓冲,因此顶点顶点着色器的数据变换不写入缓冲
glBindBuffer(GL_ARRAY_BUFFER, databuffer[flag]);
glVertexAttribPointer(positionID,4,GL_FLOAT,GL_FALSE,32,(void*)0);
glVertexAttribPointer(colorID,4,GL_FLOAT,GL_FALSE,32,(void*)16);
glDrawArrays(GL_TRIANGLES, 0, 36);
flag = !flag;//进行第二次循环

第二次循环将变换后的数据写入databuffera,将databufferb第一次变换的数据传入顶点着色器,数据进行第二次变换,但此时不显示,而是写入tf缓冲的databuffera。使用databuffera第二次变换的数据绘制图形,因为此使没开启tf缓冲,因此顶点顶点着色器的数据变换不写入缓冲。

原始数据:-0.1f,0.1f, 0.1f,1.0f,0.1f,0.1f, 0.1f,1.0f

如下是经过两次循环后tf缓冲中的数据:

image-20230102175536791

补充

如下数据每行前四个是顶点坐标数据,后四个是顶点颜色数据:

image-20230102180412855

第五个参数:步长(Stride),在连续的顶点属性组之间的间隔。上图一行8个数是一个顶点属性,所以步长为8*4=32

第六个参数:使用VBO时是缓冲对象数据存储区的字节偏移量(不使用VBO时是顶点数组指针)

颜色数据从第五个数据读起,前面有四个顶点坐标数据,因此偏移量为4*4=16

1
2
glVertexAttribPointer(positionID,4,GL_FLOAT,GL_FALSE,32,(void*)0);
glVertexAttribPointer(colorID,4,GL_FLOAT,GL_FALSE,32,(void*)16);

参考链接:https://blog.csdn.net/niu2212035673/article/details/79041437

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信