OpenGL ES 3.0、openGL 4.x新增的功能
我们总是将顶点数据发送到图形处理器,并且只在帧缓存中生成绘制的像素。如果我们想要在经过顶点着色器或几何着色器之后捕获这些顶点呢?在这一章中,我们将探讨一种方法,即transform feedback。TransformFeedback是OpenGL管线中,顶点处理(如果有几何着色器,那么就在几何着色器后)之后,图元装配之前的一个步骤。
我们已经使用了VBO(顶点缓冲区对象)来存储用于绘制操作的顶点。Transform feedback 允许着色器把顶点写回这些。
例如,您可以构建一个顶点着色器,它模拟重力并将更新后的顶点位置写回缓冲区。这样,您就不必将这些数据从图形内存传输到主内存。最重要的是,你可以从如今GPU的巨大的并行处理能力中获益。
这个特性可以用来简化像粒子模拟这样的效果
让我们从一个简单的顶点着色器开始。
const GLchar* vertexShaderSrc = R"glsl(
in float inValue;
out float outValue;
void main()
{
outValue = sqrt(inValue);
}
)glsl";
这个顶点着色器似乎没有多大意义。它没有设置一个gl_position,它只接受一个任意的浮点数作为输入。幸运的是,我们可以使用Transform feedback来捕获结果,我们马上就会看到。
GLuint shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(shader, 1, &vertexShaderSrc, nullptr);
glCompileShader(shader);
GLuint program = glCreateProgram();
glAttachShader(program, shader);
编译着色器,创建一个程序并附加着色器,但是不要调用glLinkProgram()!在链接程序之前,我们必须告诉OpenGL,我们想要捕获到一个缓冲区中的输出属性。
const GLchar* feedbackVaryings[] = { "outValue" };
glTransformFeedbackVaryings(program, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
1、Transform Feedback对象
transform feedback状态封装在一个transform feedback对象中,这个状态中包括所有记录顶点数据的缓存对象等,transform feedback对象的创建和其他的缓存对象类似,首先要创建一个对象名称,然后绑定到OpenGL环境中。
1创建对象名
GLuint glGenTransformFeedbacks(GLenum target, GLuint *id);
返回值:返回Uniforms的缓冲索引
参 数:program 着色器程序ID
uniformBlockName Uniforms块名
2绑定到OpenGL环境
GLuint glBindTransformFeedback(GLenum target, GLuint id);
返回值:返回Uniforms的缓冲索引
参 数:program 着色器程序ID
uniformBlockName Uniforms块名
3删除对象
GLuint glDeleteTransformFeedbacks(GLsizei n, const GLuint* ids);
返回值:返回Uniforms的缓冲索引
参 数:program 着色器程序ID
uniformBlockName Uniforms块名
2、Transform Feedback缓存
transform feedback对象主要用于管理将顶点捕捉到缓存对象的相关状态,这个状态中包含了链接到transform feedback缓存绑定点的缓存对象,transform feedback对象必须和一个缓存对象链接才能输出数据,它本身是不存储输出数据的(与帧缓存对线和附加到它上的纹理缓存类似)。
如果想要将整个缓存绑定到transform feedback上,可以使用函数glBindBufferBase()。
voidglBindBufferBase(GLenum target, GLint index, GLuint buffer)
功 能:将缓存绑定到transform feedback上
返回值:void
参 数:target 绑定目标必须设为GL_TRANSFORM_FEEDBACK_BUFFER
index 缓存绑定点索引,绑定点最大数与设备实现有关可以通关GL_MAX_TRANSFORM_FEEDBACK_BUFFERS的值来查询。所有的OpenGL设备实现都可以至少支持64个绑定点。
buffer 绑定的缓存对象
如果想要将缓存的部分绑定到transform feedback上,可以使用函数glBindBufferRange()。
void glBindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size)
功 能:将缓存绑定到transform feedback上
返回值:void
参 数:target 绑定目标必须设为GL_TRANSFORM_FEEDBACK_BUFFER
index 缓存绑定点索引
buffer 绑定的缓存对象
offset 起始地址
size 绑定的缓存大小
3、配置Transform Feedback变量
再transform feedback过程中顶点着色器可能会输出多个变量。与transform feedback对象所绑定的缓存需要记录那些变量,这些都需要在应用程序中设置,我们需要调用函数glTransformFeedbackVaryings()来设定。
void glTransformFeedbackVaryings(GLuint program, GLsizei count,
const GLchar **varyings, GLenum bufferMode)
功 能:设置缓存记录的变量
返回值:void
参 数:program 着色器程序
count 变量的总数
varyings 变量名数组
bufferMode 捕获变量的模式,可以是分量模式(GL_SEPARATE_ATTRIBS)
或交叉模式(GL_INTERLEAVED_ATTRIBS)
注意:函数glTransformFeedbackVaryings()所指定的变量,只有再一次链接的时候才会起效,换句话说,glTransformFeedbackVaryings()必须再着色器程序链接之前调用。
4、启动和停止Transform Feedback
transform feedback可以随时启动或停止,甚至暂停,如果启动transform feedback时,它并没有处于暂停的状态,它会重新开始将数据记录到当前绑定的ransform feedback缓存中,不过如果transform feedback正处于暂停的状态,那么再次启动它将会从之前暂停的位置开始记录。如果需要将厂家中的某些部分记录到transform feedback当中,这个特效是比较有用的。
启动
void glBeginTransformFeedback(GLenum primitiveMode);
暂停
void glPauseTransformFeedback(void);
如果当前的ransform feedback没有启用,或者处于暂停状态,
调用glPauseTransformFeedback会产生一个错误
重新启动
void glResumeTransformFeedback(void);
要重新启动一个暂停的ransform feedback,必须使用glResumeTransformFeedback。
如果使用glBeginTransformFeedback同样会产生一个错误。
停止
void glEndTransformFeedback(void);
如果已经完成了transform feedback图元的渲染,直接调用glEndTransformFeedback(),就可以重新切换到正常的渲染模式。
5、从Transform Feedback取数据
GLfloat feedback[5];
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
printf("%f %f %f %f %f\n", feedback[0], feedback[1], feedback[2], feedback[3], feedback[4]);
6、绘制Transform Feedback的内容
void glDrawTransformFeedback( GLenum mode,GLuint id);
参 数:mode 绘制模式
id Transform Feedback的标识