shader和program
Shader Object是包含单个shader的对象。它接收源代码并将之编译成object形式(类似c中的.obj)。
完成编译后,shader object可以被指定给一个program object。
Program object可附加多个shader object,在OpenGL ES中,每个program object有且仅有一个vertex shader object和一个fragment shader object。其后,program object被链接成一个“可执行文件”,最终,program object就可以用来渲染了。
创建和编译shader
创建shader:
GLuint glCreateShader(GLenum type);
type可以是GL_VERTEX_SHADER或GL_FRAGMENT_SHADER。
删除shader:
void glDeleteShader(GLuint shader);
当shader被指定给program object时,glDeleteShader并不会立即删除shader,而是将shader标记为删除,直到shader不在指定给任何program object时才会真正释放内存。
输入源代码给shader:
void glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length);
需要注意的是,实际上,并不是所有的OpenGL ES 2.0都提供编译能力,有些是要求离线编译的,这时,就要使用shader binary。
编译shader:
void glCompileShader(GLuint shader);
获取shader状态:
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params);
其中,pname可以是GL_COMPILE_STATUS、GL_DELETE_STATUS、GL_INFO_LOG_LENGTH、GL_SHADER_SOURCE_LENGTH、GL_SHADER_TYPE。
查询编译日志:
void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog);
创建和编译program
创建program:
GLuint glCreateProgram(void);
删除program:
GLuint glDeleteProgram(GLuint program);
附加shader:
void glAttachShader(GLuint program, GLuint shader);
解除附加:
void glDetachShader(GLuint program, GLuint shader);
链接程序:
void glLinkProgram(GLuint program);
获取程序状态:
void glGetProgramiv(GLuint program, GLenum pname, GLint *params);
其中,pname可以是GL_ACTIVE_ATTRIBUTES、GL_ACTIVE_ATRIBUTE_MAX_LENGTH、GL_ACTIVE_UNIFORMS、GL_ACTIVE_UNIFORM_MAX_LENGTH、GL_ATTACHED_SHADERS、GL_DELETE_STATUS、GL_INFO_LOG_LENGTH、GL_LINK_STATUS、GL_VALIDATE_STATUS。
查询链接日志:
void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length, GLchar **infoLog);
验证程序合法性,例如,是否所有sampler都绑定了纹理:
void glValidateProgram(GLuint program);
设置程序为当前活跃程序:
void glUseProgram(GLuint program);
uniform和attribute
Uniform是保存应用通过OpenGL ES 2.0 API传递给shader的只读常数值的变量。
Uniform会被整个program共享。
如果一个uniform在vertex和fragment shader中都被使用,它的类型必须相同,它的值对所有shader来说也是一致的。
在link阶段,链接器会为所有活跃的uniform分配位置,uniform的位置被用作应用给uniform加载数值的标识符。
读取和设置uniform
仅当被使用的时候,uniform才被认为有效,即,未被使用的uniform会被链接器优化掉。
查询有效的uniform:
void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name);
查询uniform的location:
GLuint glGetUniformLocation(GLuint program, char *name);
为uniform加载数值:
// float
void glUniform1f(GLint location, GLfloat x);
void glUniform2f(GLint location, GLfloat x, GLfloat y);
void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z);
void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
// float vec
void glUniform1fv(GLint location, const GLfloat *v);
void glUniform2fv(GLint location, const GLfloat *v);
void glUniform3fv(GLint location, const GLfloat *v);
void glUniform4fv(GLint location, const GLfloat *v);
// int
void glUniform1i(GLint location, GLint x);
void glUniform2i(GLint location, GLint x, GLint y);
void glUniform3i(GLint location, GLint x, GLint y, GLint z);
void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w);
// int vec
void glUniform1iv(GLint location, const GLint *v);
void glUniform2iv(GLint location, const GLint *v);
void glUniform3iv(GLint location, const GLint *v);
void glUniform4iv(GLint location, const GLint *v);
// float matrix, notice: no int matrix
void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
注意:
- 当uniform为数组时,仅可使用vec形式的函数,且会一次性加载全部的分量的数值;
- 设置uniform的函数的参数不包括program,它只作用于当前使用的program,且一旦设置,就会保存在该program中,即使它不再是当前使用的program。
获取和设置attribute
获取attribute信息的方法和uniform非常相似。
而设置attribute需要对图元以及vertex shader有更深的理解,待第六章讲解。
注意:Uniform的location是linker决定的,因此设置uniform时需要查询其位置后设置;attribute的location可以通过glBindAttribteLocation来指定。
Shader编译器和Shader二进制文件
由于再现编译、优化需要花费CPU时间和内存,spec允许实现仅支持二进制shader。
OpenGL ES 2.0 vendor提供离线工具将shader源代码编译成实现可识别的二进制格式。
spec没有对二进制格式进行规定,vendor可执行设计。
可以使用glGetBooleanv查询GL_SHADER_COMPILER来确定实现是否支持在线编译。
可以使用glGetIntegerv查询GL_NUM_SHADER_BINARY_FORMATS查询实现支持的二进制格式数量;
可以使用glGetIntegerv查询GL_SHADER_BINARY_FORMATS查询实现具体支持的二进制格式。
当编译完成后,可以给实现发送可以释放编译器资源的提示:
void glRleaseShaderCompiler(void);
加载二进制文件到shader:
void glShaderBinary(GLint n, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLint length);
一个二进制文件中可包含一个或多个shader,且可以同时存在vertex和fragment shader。
verdor可以要求vertex shader和fragment分别给定,然后在线link,也可以要求shader的link也离线完成。
加载完二进制文件的shader状态,除了不能读回源码外,和编译后几乎一样。