OpenGL ES (三)着色器和程序

OpenGL ES学习系列文章:
上一篇:OpenGL ES (二)EGL介绍和使用
下一篇:OpenGL ES (二)EGL介绍和使用

前言

在OpenGL ES 3.0程序中,Shader和Program是两个重要的概念,至少需要创建一个顶点Shader对象、一个片段Shader对象和一个Program对象,才能用着色器进行渲染,理解Shader对象和Program对象的最佳方式是将它们比作C语言的编译器和链接程序,从Shader的创建到Program的链接共六个基本步骤,创建Shader、加载Shader源码、编译Shader、创建Program、绑定Program与Shader、链接Program,下面介绍Shader和Program的创建方法及相关概念。

glCreateShader
glShaderSource
glCompileShader
glCreateProgram
glAttachShader
glLinkProgram
glUseProgram

1.创建Shader

GLuint glCreateShader(	GLenum shaderType);

创建着色器使用glCreateShader,成功时返回一个空的着色器对象句柄,一个正整数,失败时返回0,shaderType有误时产生错误GL_INVALID_ENUM,shaderType可以是:

GL_VERTEX_SHADER 			// vertex
GL_FRAGMENT_SHADER		    // fragment
GL_COMPUTE_SHADER 			// from GL ES 3.1
GL_TESS_CONTROL_SHADER	    // from GL ES 3.2
GL_TESS_EVALUATION_SHADER   // from GL ES 3.2
GL_GEOMETRY_SHADER 			// from GL ES 3.2

有了glCreateShader来创建Shader,必然有删除shader的方法,glDeleteShader用来删除着色器,包括内存释放和shader句柄释放,如果这个着色器对象attach到了一个程序对象,那么这个着色器对象将做个删除标记而不会立即删除,等到不再attach任何程序时再删除,参数shader无效时产生错误GL_INVALID_VALUE。

void glDeleteShader(GLuint shader);

对于一个着色器对象,如果想要判断其是否具有删除标记,可以使用glGetShaderiv函数,见7.1小节

2.加载Shader源码

void glShaderSource(
	GLuint shader, 				//指定要替换其源代码的着色器对象的句柄。
    GLsizei count,				//指定string和length数组中的元素数。
    const GLchar **string,		//指向包含要加载到着色器的源代码的字符串的指针数组。
    const GLint *length);		//指定字符串长度的数组

glShaderSource将shader中的源代码设置为string指定的字符串数组中的源代码。先前存储在着色器对象中的所有源代码都将被完全替换。数组中的字符串数由count指定。如果length为NULL,则认为着色器源码以NULL结尾。如果length不是NULL,则它指向一个数组,该数组包含字符串的每个对应元素的字符串长度。length数组中的每个元素可以包含相应字符串的长度(空字符不算为字符串长度的一部分)或小于0的值,以指示字符串以空结尾。此时不扫描或解析源代码字符串;它们只是复制到指定的着色器对象中。

3.编译Shader

void glCompileShader(GLuint shader);

成功加载着色器源码之后,通过glCompileShader编译着色器对象,处理上一步加载的着色器源码字符串,shader为前面创建的着色器对象句柄,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

当OpenGL ES编译和绑定着色器时,着色器代码通常解析为某种中间表现形式,这和大部分编译语言相同,如抽象语法树,编译器必须将抽象形式转换为硬件的机器指令,理想状态下,这个编译器还应该进行大量的优化,如无用代码删除、常量传播等,进行这些工作需要付出代价,主要是CPU时间和内存。OpenGL ES 3.0实现必须支持在线着色器编译,用glGetBooleanv检索的GL_SHADER_COMPILER值必须是GL_TRUE,可以指定着色器使用glShaderSource,还可以尝试缓解着色器编译对资源的影响,也就是说,一旦完成了应用程序中着色器的编译,就可以调用如下glReleaseShaderCompiler,这个函数提示OpenGL ES实现已经完成了着色器编译的工作,可以释放它的资源了。需要注意的是,这个函数只是一个提示,如果决定用glCompileShader编译更多的着色器,那么OpenGL ES实现需要重新为编译器分配资源。

4. 创建Program

GLuint glCreateProgram(void);
GLuint glDeleteProgram(void);

创建程序对象使用glCreateProgram,glCreateProgram创建一个空的程序对象,一个非零值,失败时返回0。与glCreateProgram对应的有个glDeleteProgram,用于删除程序对象,包括内存释放和program句柄释放,如果这个程序对象在使用中,那么将作个删除标记而不会立即删除,等到不再使用时再删除,其中的着色器对象将自动detach,着色器对象有删除标记就删除,否则不删除。参数program无效时产生错误GL_INVALID_VALUE。

扫描二维码关注公众号,回复: 13523986 查看本文章

判断一个程序对象是否具有删除标记,可以使用glGetProgramiv函数,详见7.5小节

5. 绑定Program和Shader

void glAttachShader(GLuint program, GLuint shader);
void glDetachShader(GLuint program, GLuint shader);

创建了程序对象之后,通过glAttachShader将其绑定到一个着色器对象,program和shader为对应的程序对象和着色器对象,同一种类型的着色器对象只能绑定一次,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。与glAttachShader对应的有个glDetachShader,用于解除绑定,program和shader为对应的程序对象和着色器对象,如果着色器对象有删除标记且没有被其它地方使用将删除,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

6.链接Program

void glLinkProgram(GLuint program);

程序对象与着色器对象attach之后,使用glLinkProgram链接程序对象,这是使用程序对象前的最后一步,参数program为需要链接的程序对象句柄,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。程序对象链接成功之后,所有活动的用户定义的uniform变量都被初始化为0而且被设置一个位置,这个位置使用glGetUniformLocation获取,所有活动的命名uniform块中的uniform变量都被设置一个偏移量,包括数组和矩阵,所有活动的用户定义的attribute变量没有绑定到一般的顶点属性索引时将在此时绑定一个。链接操作负责生成最终的可执行程序,链接阶段是生成在硬件上运行的最终指令的时候,链接程序将检查各种对象的数量,确保成功链接,例如,链接程序将确保顶点着色器写入片段着色器使用的所有顶点着色器输出变量并用相同的类型声明,确保任何在顶点和片段着色器中都声明的统一变量和统一变量缓冲区的类型相符,确保最终的程序符合具体实现的限制,如属性、统一变量、输入输出着色器变量的数量等。链接程序对象可能会失败,常见原因可参照https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glLinkProgram.xhtml。

7. 使用着色器程序

void glUseProgram(GLuint program);

最后,成功链接程序对象之后,就可以使用这个着色器程序了,后面在添加一些顶点数据就可以进行图元绘制了。glUseProgram把这个program程序对象安装到当前渲染状态的一部分,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

8. 其他可能会用到的方法

8.1 glGetShaderiv

此函数从shader中返回一个参数,可用于检查shader状态,shader是着色器对象句柄,pname为待检查的Shader状态,params返回检查的Shader状态。在第1小节中想要判断shader是否具有删除标记,就可以传入pname=GL_DELETE_STATUS,就会返回GL_TRUE 或GL_FALSE

void glGetShaderiv(GLuint shader,GLenum pname,GLint *params);
// shader是着色器对象句柄
// pname:指定对象参数,接收的参数名称有:
//		GL_SHADER_TYPE, 返回GL_VERTEX_SHADER 或 GL_FRAGMENT_SHADER 
//		GL_DELETE_STATUS, 返回GL_TRUE (有删除标记) 或 GL_FALSE 
//		GL_COMPILE_STATUS, 返回GL_TRUE (编译成功) 或 GL_FALSE
//		GL_INFO_LOG_LENGTH, 
//		GL_SHADER_SOURCE_LENGTH.
// params: 返回要求的对象参数

8.2 glGetShaderInfoLog

此函数返回着色器对象的日志信息,shader为着色器对象句柄,maxlength指定用于存储返回的信息日志的字符缓冲区的大小。length返回获取的日志信息的长度,不包括null结尾的字符,可以为NULL.infoLog是返回信息日志的字符数组。

void glGetShaderInfoLog(
		GLuint shader,
    	GLsizei maxLength,
    	GLsizei *length,
   	 	GLchar *infoLog);

##8.3 glIsShader
此函数用于判断是否为着色器对象

GLboolean glIsShader(GLuint shader);

##8.4 glGetShaderSource
此函数用于返回着色器源码的字符串表示

void glGetShaderSource(
		GLuint shader,				//着色器句柄
    	GLsizei bufSize,			//指定用于存储返回的源代码字符串的字符缓冲区的大小。
    	GLsizei *length,			//源代码中返回的字符串的长度
    	GLchar *source);			//用于返回源代码字符串的字符数组

8.5 glGetProgramiv

此函数从program中返回一个参数,可用于检查Program状态,用法同glGetShaderiv

void glGetProgramiv(
	GLuint program,
    GLenum pname,
    GLint *params);

详细用法及参数见khronos::glGetProgramiv

8.6 glGetProgramInfoLog

此函数返回program对象的日志信息,用法同glGetShaderInfoLog

void glGetShaderInfoLog(
		GLuint shader,
    	GLsizei maxLength,
    	GLsizei *length,
   	 	GLchar *infoLog);

详细用法见khronos::glGetProgramInfoLog

猜你喜欢

转载自blog.csdn.net/mataojie/article/details/115485153