《OGL dev》Etay Meiri Tutorial 04 - Shaders 笔记
OpenGL的可编程管线

vertex processor对应vertex shader(顶点着色器)。
- 顶点的数量取决于draw call的参数,即
glDrawArrays(GL_TRIANGLES, 0, 3);
的最后一个参数。 - 不知道图元的拓扑。
- 无法丢弃顶点。
geometry processor对应geometry shader(几何着色器)。
- 接收图元的所有顶点和邻近的顶点。
- 能够切换到非draw call选择的拓扑。
- 一个顶点能够输出多个拓扑。(例如,广告牌:1个顶点生成2个三角形)
clipper(裁剪)
- 按近Z和远Z的平面进行裁剪,用户自定义裁剪平面。
- 裁剪至normalized box。
clipper留下的顶点,先进行屏幕映射,之后rasterizer(光栅化)依据它们的拓扑渲染到屏幕。
rasterizer会找出依据拓扑的所有顶点。
fragment processor对应fragment shader(片段着色器)。
- 每一个顶点,rasterizer都调用fragment processor。
- 决定像素的颜色。
- 可以丢弃像素。
- 可以改变Z值,影响后续的Z测试。
vertex、geometry和fragment processors是可选、可编程的。不绑定shader会执行默认的功能。
clipper是固定的。
调用shader
- 创建shader对象:
GLuint ShaderObj = glCreateShader(ShaderType);
。ShaderType可选 GL_VERTEX_SHADER和GL_FRAGMENT_SHADER。 - 为shader对象指定源代码:
glShaderSource(ShaderObj, 1, p, Lengths);
。OpenGL允许shader源代码分散在多个char *中。因此p是char * [],Lengths是GLint[]对应每个p的元素的长度(不包含'\0'),第二个参数1表示p的元素个数。 - 编译shader:
glCompileShader(ShaderObj);
。 - 检查编译错误并打印:
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success); glGetShaderInfoLog(ShaderObj, sizeof(InfoLog), NULL, InfoLog);
。GL_COMPILE_STATUS表示编译,编译失败时success为0。InfoLog是char *用于存储错误信息。 - 创建program对象:
GLuint ShaderProgram = glCreateProgram();
。program对象用于附加shader。 - 将编译好的shader对象附加到program对象:
glAttachShader(ShaderProgram, ShaderObj);
。 - 链接shader:
glLinkProgram(ShaderProgram);
。链接shader会对shader进行优化。例如:将输出法线的vertex shader和忽略法线的fragment shader配对,driver中的GLSL编译器会删除法线。因此,相同的shader链接后可能生成不同的shader。 - 检查链接错误并打印:
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success); glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
。GL_LINK_STATUS表示链接,失败时success为0。ErrorLog是char *用于存储错误信息。 - 验证program:
glValidateProgram(ShaderProgram);
。检查program是否能在当前状态下的管线中执行。 - 检查验证错误并打印:与检查链接错误相同,仅将glGetProgramiv的第二个参数改为GL_VALIDATE_STATUS,表示验证。
- 将propram设置到管线状态中:
glUseProgram(ShaderProgram);
。这个propram一直生效,直到glUseProgram调用另一个propram替代它。glUseProgram(NULL)表示使用固定功能管线。如果propram没有包含所有类型的shader,其余的都将使用固定功能。
shader链接到program对象之后,依旧可以被删除。
- 调用
void glDeleteShader(GLuint shader)
,- 如果一个shader没有附加到任何program,它将被删除,与glCreateShader相反。
- 如果shader已附加到program,将shader标记为删除。OpenGL使用引用计数管理shader,因此不会直接删除。
- 调用
void glDetachShader(GLuint program, GLuint shader);
,将shader从program中分离,与glAttachShader相反。如果一个shader被标记为删除,分离后没有附加到任何program,即引用计数归0,glDetachShader将删除这个shader。
编写shader
#version 330
,表示GLSL版本为3.3,编译不支持则报错。
layout (location = 0) in vec3 Position;
- 声明了一个vertex specific attribute,名称为Position,它的值来自于buffer。
- vec3表示Position是一个3 float的向量。
layout (location = 0)
将buffer中的attribute name与shader中的attribute绑定。0是glVertexAttributePointer的第一个参数,表示坐标。
- 0是硬编码的值,复杂的程序会仅在shader中声明
in vec3 Position
,然后运行时调用glGetAttribLocation查询它的位置。但教程没有举例说明具体用法。
void main()
是shader的入口,一个shader必须有且仅有一个。
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
,gl_Position是一个内置变量,它包含齐次顶点坐标(X、Y、Z和W)。因为屏幕映射过程中会使用透视除法,将gl_Position的所有坐标都除以W,所以这里将W设为1。
out vec4 FragColor;
- 声明变量FragColor,FragColor的值将被光栅器接收写入到帧缓冲中。
- vec4表示4 float,分别表示R、G、B和A。