Transform Feedback
此部分内容参考自另一份教程https://open.gl/introduction
该教程内容没有learnOpenGL丰富,但是有自己独特的内容,比如使用glew,transform feedback,以及提到了如何使用OpenGL做和画图不相关的通用编程。
一、基本用法
transform feedback有点通用编程的意思了,它的作用是读取shader处理后的vertex数据而和fragment无关。如果有geometry shader那么读取的vertex就是geometry输出的结果;如果没有就是vertex shader输出的结果。由于和fragment没有关系,为了节约不必要的计算步骤,我们使用glEnable(GL_RASTERIZER_DISCARD);
来取消栅格化及之后的pipeline。
transform feedback使用简单。首先要准备一个array buffer object,用于存储接受的值。我们要给该vbo开辟好足够的显存空间。
GLuint tbo;
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLint) * 20, nullptr, GL_STATIC_READ); // nullptr, only for space
glBindBuffer(GL_ARRAY_BUFFER, 0);
不需要初始化。然后正常准备输入vertex。
其次一个要点就是要制定接受shader输出的哪些数据。我们在相应的shader里面要规定输出,但还需要指定哪些输出需要被捕捉(也包含捕捉顺序)。我们使用char **
,即字符串的数列来指定要捕获变量的名字,比如有3个,分别是GLchar * varyingNames[] = {"aaa", "bbb", "ccc"}
。然后使用
glTransformFeedbackVaryings(program, 3, varyingNames, GL_INTERLEAVED_ATTRIBS);
来具体指定。这一步必须在shader Link之前,创建之后,否则无效(pangolin使用的NV版本的可以在之后,好用许多)。
最后一个要点是绑定接收的Buffer
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
然后激活transform feedback模式,画图,然后关掉模式:
glBeginTransformFeedback(GL_POINTS); // model is same with vertex output
glDrawArrays(GL_POINTS, 0, 20); // same with vertex input
glEndTransformFeedback();
最后,我们可以拿tbo继续当做输入去画图,也可以读取到内存:
GLint feedback[20];
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
二、Feedback Buffer Mode
目前我们可以把一个或几个attribute按照interleaved方式写入一个vbo当中,那么如何改变输出的格式呢?接下来的内容参考https://zhuanlan.zhihu.com/p/91533547并经过了实验验证。
1. 不同attribute进入不同Buffer
还是比如我们有三个attribute,shader Link时候的方法都一样,但是模式要选择separate attribute:
glTransformFeedbackVaryings(program, 3, varyingNames, GL_SEPARATE_ATTRIBS);
当画图前绑定Buffer的时候要依次绑定到目标不同的节点上:
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, tbo1);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, tbo2);
其中第一个参数是目标target,第二个是节点(有最大限制),最后一个是我们创建的Buffer。然后激活-画图-终止激活。这样就得到了三个Buffer分别存储不同的attribute。
2. 多个Interleaved Buffer
如果有更高的要求,比如前俩个attribute以interleaved方式存储在一个Buffer里,最后一个放在里一个里面,可以选用占位字符串帮助我们转换Buffer。这时mode必须仍然是GL_INTERLEAVED_ATTRIBS
,但是varingNames[] = {"aaa", "bbb", "gl_NextBuffer", "ccc"}
。那个"gl_NextBuffer"
是告诉shader往下一个Buffer里面写。
3. attribute之间有间隔
和2一样,但是占位字符串变成varingNames[] = {"aaa", "bbb", "gl_SkipComponents1", "ccc"}
。其中最后的数字取1,2,3,4,代表跳过的单位数(我猜是byte,没试过)
【为什么没有vao就不行?为什么绑定fid会崩溃?】
第一个这个问题,https://learnopengl.com/Getting-started/Hello-Triangle有说明:在Core版本里,绑定vao已经是必须选项。pangolin会有各种乱七八糟版本。
第二个因为版本不对,生成fid的函数是4.0之后才有的,把版本(glfwWindowHint
)一改就可以了,不过在这里好像没什么用。