OpenGL ES简介
OpenGL ES(OpenGL for Embedded Systems)是基于OpenGL的3D图形应用程序编程接口,主要是为手机、平板等手持和嵌入式设备设计。其支持的平台包括Android、iOS、Linux和Windows等。
顶点着色器
从编程角度,顶点着色器是一段代码指令,用于描述针对于顶点的着色器程序源代码或可执行文件。在顶点着色器中包含以下几部分内容:
- 顶点着色器输入。顶点着色器输入是由顶点数组提供的顶点数据,通过传入顶点着色器能够做矩阵变换、顶点颜色或照明等计算,从而输出进行后续供后续的图元装配、光栅化,再到片段着色器的运行。
- 统一变量。顶点着色器中不变的数据,通常会传入计算好的旋转矩阵,从而实现模型-视图-投影坐标的转换
- 采样器。在后续的纹理处理中会用到的特殊变量。纹理获取不会传递指针指向GLSL,而是通过纹理ID绑定的方式在内存中查找。代表顶点着色器使用纹理的特殊统一变量类型;
顶点着色器示例
其中第2行是指明使用的着色器版本;第3行和第4行分别指明了位置和颜色的属性位置索引为1和2;第5行描述了统一变量
u_MVPMatrix
,开发人员可以自定义的将计算好的模型-视图-投影矩阵传入;第6行声明了一个描述顶点纹理坐标的输出变量,存储了每个顶点纹理输出。其中gl_Position
是内建变量,变换后的顶点坐标都需要写入这个变量;第9行则是通过矩阵变换后的坐标输出写入gl_Position
;第10行则是读入顶点纹理坐标并写入到v_texCoord
变量中,供后续片段着色器写入使用。
片段着色器
片段着色器中包含以下几部分内容:
- 顶点着色器中生成的插值数据输入。顶点着色器的输出通过图元变换后传递给片段着色器作为输入变量。
- 统一变量。这些变量在片段着色器上不会变化。
- 采样器。片段着色器使用的特殊变量,用于访问着色器中的纹理图像。
片段着色器示例
其中第2行指明了着色器语言版本;第3行设定精度限定符;第4行则是声明一个通过顶点着色器输出后,经过图元变换后作为片段着色器的输入变量;第5行指明输出颜色的位置属性索引为0;第6行声明一个统一变量,可供后续设定输入纹理;第9行中,
s_Texturemap
是一个类型为sampler2D
的统一变量,内建函数texture
会从纹理贴图中读取加载相应的纹理。开发过程中则包括使用glActiveTexure
激活当前纹理单元,以便后续glBindTexture
调用将纹理绑定到当前活动单元。
着色器装载和使用
要进行着色器渲染需要创建两个基本对象,分别是着色器对象和程序对象,以下将分成4部分介绍着色器渲染所需的所有前置步骤:
- 创建和编译着色器
- 创建和链接程序
- 统一变量的获取和设置
- 属性的获取和设置
1. 创建和编译着色器
图中1使用glCreateShader(GLenum shader)
用于创建一个着色器对象;2则是使用glShaderSource(GLuint shader,GLsizei count,const GLchar* const *string,const GLint *length)
将着色器源代码提供给该着色器;3是在指定着色器后进行编译,使用glCompileShader(GLuint shader)
。该环节后则完成了着色器的创建和编译,后续代码第4步则是提供查询编译是否错误和着色器对象的相关信息,使用glGetShaderiv(GLuint shader, GLenum pname,GLint *params)
;此外,如果想进一步检索信息日志,可以使用第5步中的glGetShaderInfoLog(GLuint shader, GLsizei maxLength,GLsizei *length, GLchar *infoLog)
获取。最后如果着色器对象没有完成创建或其他错误,可使用第6步中的glDeleteShader(GLuint shader)
删除着色器对象的句柄,释放内存。
2. 创建和链接程序
第1步中,程序对象创建通过glCreateProgram()
方法创建,返回一个指向对象的句柄;第2步中使用glAttachShade(GLuint program, GLuint shader)
将程序对象与着色器进行连接,值得注意的是每个程序对象需要连接一个顶点着色器和一个片段着色器;完成程序对象与着色器的连接后可以通过第3步glLinkProgram(GLuint program)
完成程序对象的链接,即生成最终可执行程序;第4步则是完成链接后可使程序对象与着色器断开连接,使用glDetachShader(GLuint program, GLuint shader)
;第5和6步与上文的着色器信息查询相同,同样的在程序链接完成后可以通过glGetProgramiv(GLuint program, GLenum pname, GLint *params)
检查链接状态,并通过glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length, GLchar *infoLog)
获取程序的相关信息日志;最后第7步如果程序对象创建错误或未成功链接,可使用glDeleteProgram(GLuint program)
删除程序对象。
3. 统一变量的获取和设置
通常我们会在着色器中自定义统一变量,在后续代码编写过程中加载所需要的自定义数据,最常见的如u_MVPMatrix
,通过提前计算好的坐标转换矩阵,在输入坐标时可将物体的世界坐标转换成视口坐标。具体方法名为glGetUniformLocation(GLuint program, const GLchar* name)
,其中name
指明了统一变量的位置,和着色器源码中的名称相同。通过该方法返回的GLint位置返回值可以作为加载统一变量方法glUniform1f
中的参数进行赋值,其中类比不同的统一变量,加载方法也有不同的函数,如glUniform1f(GLint location,GLfloat x)
、glUniform3f(GLint location, GLfloat x, GLfloat y,GLfloat z)
等等。以下图代码为例,1为着色器中定义的统一变量,2为加载相关统一变量的方法。
4. 属性的获取和设置
和统一变量相同,程序对象还需要设置顶点属性,可以通过glGetActiveAttrib
查找某个属性特性,并通过glBindAttribLocation
将通用顶点属性索引绑定到顶点着色器中的一个属性变量。