第五章 视口变换、剪切与反馈
观察视图
OpenGL的坐标系统:
OpenGL变换
设置远近平面
void glDepthRange(GLclampd near, GLclampd far);
void glDepthRangef(GLclampf near, GLclampd far);
近平面和远平面的值也就是深度缓存中所保存的最小值和最大值。默认情况下分别取0.0和1.0,所设置的范围是[0, 1]之间的。
设置绘制区域
void glViewport(GLint x, GLint y, GLint width, GLint height);
设置窗口中的渲染范围。
用户剪切
可以使用着色器内置变量gl_clipDistance来设置用户裁剪平面,需要自行设置它的内容,它的值会经过插值设置给顶点之间的片元。
#version 330 core
uniform vec4 Plane; // 平面方程 Ax + By + Cz + D = 0的系数
in vec4 Vertex;
float gl_clipDistance[1];
void main()
{
gl_clipDistance[0] = dot(Vertex, Plane);
}
这个变量的含义是顶点距离平面的距离,0表示在平面上,整数表示在平面内测,负值表示在平面外侧,OpenGL会抛弃距离小于0的片元。
所有使用gl_clipDistance的着色器必须将其声明为同样大小。
在使用之前需要使用glEnable(GL_CLIP_PLANE0)来激活指定剪切平面。
transform feedback
transform feedback是图元装配和光栅化之前的步骤,可以将即将装配为图元的顶点部分或者全部传递到缓存对象中。
创建transform feedback对象
transform feedback状态保存在transform feedback对象中,状态包括记录顶点数据的缓存对象,用于标识缓存对象充满程度的计数器,以及用于标识transform feedback当前是否启用的状态量。
void glGenTransformFeedbacks(GLsizei n, GLuint * ids);
绑定transform feedback
void glBindTransformFeedback(GLenum target, GLuint id);
target必须是GL_TRANSFORM_FEEDBACK。
id是否是transform feedback对象名
void glIsTransformFeedback(GLuint id);
删除transform feedback对象
void glDeleteTransformFeedbacks(GLsizei n, const GLuint* ids);
绑定缓存对象到transform feedback缓存绑定点
void glBindBufferBase(GLenum target, GLuint index, GLuint buffer);
将名为buffer的缓存对象绑定到目标target的某个绑定点上,其索引通过index设置,同时绑定到target所设置的一般缓存绑定点上。
绑定缓存的一部分
void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
指定记录变量
void glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar** varyings, GLenum bufferMode)
program指定所用程序
count, varings指定输出变量名
bufferMode标记不活的变量是如何分配的。GL_INTERLEAVED_ATTRIBS表示所有变量是一个接着一个记录在当前的transform feedback的第一个绑定点的缓存对象里;GL_SEPARATE_ATTRIBS表示每个变量都会记录到一个单独的缓存对象中。
不同stream的输出不能被保存在同一个buffer中。
需要在链接程序之前调用才能生效。
GL_INTERLEAVED_ATTRIBS存储方式:
GL_SEPARATE_ATTRIBS存储方式:
如果需要自定义变量在缓存中的存储方式,可以使用gl_SkipComponents{1,2,3,4}和gl_NextBuffer,如果遇到任何一个gl_SkipComponents变量,会在transform feedback缓存中留出一个指定数量的空隙,只有在GL_INTERLEAVED_ATTRIBS时才能使用。
如果OpenGL遇到gl_NextBuffer那么它会将变量传递到当前绑定的下一个transform feedback缓存中。如果在GL_SEPARATE_ATTRIBS时我们遇到gl_NextBuffer或者在GL_INTERLEAVED_ATTRIBS遇到两个或者多个gl_NextBuffer,那么他将会直接跳过当前绑定点,并且在当前绑定的缓存中不会记录任何数据
static const char* const vars[] =
{
"foo", "gl_SkipComponents1", "bar", "gl_SkipComponents2",
"gl_NextBuffer",
"gl_SkipComponents4", "baz", "gl_SkipComponents2",
"gl_NextBuffer",
"gl_NextBuffer",
"iron", "gl_SkipComponents3", "copper"
};
glTransformFeedvackVaryings(prog, sizeof(vars) / sizeof(vars[0]), varyings, GL_INTERLEAVED_ATTRIBS);
glLinkProgram(prog);
获得的存储结果:
启动transform feedback
void glBeginTransformFeedback(GLenum primitiveMode);
设置transform feedback准备记录的图元类型。只能用GL_POINTS、GL_LINES和GL_TRIANGLES,之后的绘制命令使用的图元类型必须与它相符合,或者几何着色器的输出类型必须与primitiveMode相符。
暂停transform feedback
void glPauseTransformFeedback(void);
tranform feed暂停之后,仍然是启动状态,但是不会像缓存中记录任何数据。
暂停模式下的限制:
- 当前绑定的transform feedback对象不可改变
- 不允许其他缓存绑定到GL_TRANSFORM_FEEDBACK_BUFFER的绑定点
- 当前的程序对象不能改变。
如果当前绑定的transform feedback没有启动或者已经处于暂停状态,那么暂停会产生一个错误。
重启transform feedback
void glResumeTransformFeedback()
完成停止transform feedback渲染
void glEndTransformFeedback(void);
对于double类型的值,需要特别处理:
- 每一个双精度类型值,都需要8个字节。
- 包含双进度类型值的结构必须以8个字节对齐
例子:
out DataBlock
{
float var1;
dvec2 someDoubles;
float var3;
};
const char *varyings[] =
{
"DataBlock.var1",
"gl_SkipComponents1", //Padding the next component to 8-byte alignment.
"DataBlock.someDoubles",
"DataBlock.var3",
"gl_SkipComponents1", //Padding out the entire vertex structure to 8-byte alignment.
};
着色器内部指定
- xfb_offset: 指定变量从当前顶点开始的位置偏移,单位是字节
- xfb_buffer: 指定输出到的buffer索引
- xfb_stride: 指定变量的长度,最小是4