OpenGL ES 3.0学习笔记-着色器和程序

概述

如果需要使用着色器进行渲染的话,则首先必须有两个对象,分别是着色器对象及程序对象。那么如何理解这两者呢?可以将其理解为C语言的编译器和链接器。

流程是这样的,源代码中提供着色器对象,然后着色器被编译成一个目标形式,再然后链接到一个程序对象。一个程序对象对应多个着色器对象,每个程序对象必须有一个顶点着色器和一个片段着色器。

流程如下:

1.创建一个顶点着色器和一个片段着色器

2.将源代码连接到每个着色器对象

3.编译着色器对象

4.创建一个程序对象

5.将上面得到的着色器对象链接进程序对象中

6.链接程序对象

创建和编译一个着色器

创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );
if ( shader == 0 )
{
return 0;
}
//type
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31

删除

1
glDeleteShader ( shader );

删除着色器对象的句柄

注:如果着色器已经链接到程序对象中的话,这时候直接调用glDeleteShader不会立刻去删除着色器,而是将其标注,等到着色器不在连接到任何程序对象时,其就会被删除

提供着色器源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void glShaderSource(GLuint shader, //指向着色器的句柄
GLsizei count, //着色器源字符串的数量,虽然每个着色器可以有多个源字符串组成,但是每个着色器只有一个main函数
const GLchar *const*string, //指向保存数量为count的着色器源字符串的数组指针
const GLint *length //指向保存了每个着色器字符串大小且元素大小为count的整数数组指针
)
char vShaderStr[] =
#version 300 es
layout(location = 0) in vec4 vPosition;
void main()
{
gl_Position = vPosition;
}
glShaderSource (shader, 1, &shaderSrc, NULL );

编译着色器

1
2
// Compile the shader
glCompileShader ( shader );

调用glCompileShader将编译已经保存在着色器对象的着色器源代码。和常规的语言编译器一样,编译之后你想知道的第一件事情是有没有错误。你可以使用glGetShaderiv查询查询这一信息和其他有关着色器对象的信息。

检测着色器是否成功编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
esLogMessage ( "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;

创建和链接程序

创建程序

1
2
3
4
GLuint programObject;
// Create the program object
programObject = glCreateProgram ( );

删除程序

1
glDeleteProgram ( programObject );

关联着色器和程序

1
2
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );

注:对于程序对象和着色器对象的连接没有具体的时间要求,但是没有程序对象只能有一个顶点着色器和片段着色器与之连接

断开着色器和程序

1
void glDetachShader(GLuint program, GLuint shader)

链接着色器和程序

1
2
// Link the program
glLinkProgram ( programObject );

上述的工作都已经完成了之后需要链接程序的对象了。连接操作负责生成最终的可执行程序。在连接的时候将检查各种对象的数量,确保可以链接成功。

检测链接是否成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked )
{
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char *infoLog = malloc ( sizeof ( char ) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
esLogMessage ( "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return FALSE;
}

设置程序对象为活动对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Draw ( ESContext *esContext )
{
UserData *userData = esContext->userData;
GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
// Use the program object
glUseProgram ( userData->programObject );
// Load the vertex data
glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray ( 0 );
glDrawArrays ( GL_TRIANGLES, 0, 3 );
}