OpenGL ES 2.0编程指导阅读笔记(四)着色器和程序
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状态,除了不能读回源码外,和编译后几乎一样。
本文作者:belatedluck
本文链接:https://www.cnblogs.com/belatedluck/p/17046877.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步