首先,这是封装了着色器编译绑定的流程,还有涉及顶点属性和统一值等。

这里涉及到以下opengGL的函数:

  • glCreateShader
  • glShaderSource
  • glCompileShader
  • glCreateProgram
  • glAttachShader
  • glLinkProgram
  • glDeleteShader

以下是一段关乎着色器的代码段,引用自OpenGL超级宝典(第六版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
GLuint vertex_shader;
GLuint fragment_shader;
GLuint program;

// Source code for vertex shader
static const GLchar * vertex_shader_source[] =
{
"#version 430 core \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = vec4(0.0, 0.0, 0.5, 1.0); \n"
" \n"
"} \n"
};

// Source code for fragment shader
static const GLchar * fragment_shader_source[] =
{
"#version 430 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};

// Create and compile vertex shader
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, vertex_shader_source, NULL);
glCompileShader(vertex_shader);

// Create and compile fragment shader
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, fragment_shader_source, NULL);
glCompileShader(fragment_shader);

// Create program, attach shaders to it, and link it
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);

// Delete the shaders as the program has them now
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);

...

从上面代码可以大致了解如何使用着色器的一个流程。

路径

1
2
2d/shaders/CCGLProgram.h
2d/shaders/CCGLProgram.cpp

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private:
/* 主要是 */
GLuint _program;
GLuint _vertShader;
GLuint _fragShader;
GLint _uniforms[UNIFORM_MAX];
struct _hashUniformEntry* _hashForUniforms;
bool _hasShaderCompiler;

struct flag_struct {
unsigned int usesTime:1;
unsigned int usesMVP:1;
unsigned int usesMV:1;
unsigned int usesP:1;
unsigned int usesRandom:1;

// handy way to initialize the bitfield
flag_struct() { memset(this, 0, sizeof(*this)); }
} _flags;

public:
static const GLuint _maxMaterialIDNumber;

我们主要可以看见头文件中的成员变量中保存了,顶点着色器描述字,片段着色器描述字,统一值数组等。

创建初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
_program = glCreateProgram();
CHECK_GL_ERROR_DEBUG();

_vertShader = _fragShader = 0;

if (vShaderByteArray) {
if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray)) {
CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
return false;
}
}

// Create and compile fragment shader
if (fShaderByteArray) {
if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray)) {
CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
return false;
}
}

if (_vertShader) {
glAttachShader(_program, _vertShader);
}
CHECK_GL_ERROR_DEBUG();

if (_fragShader) {
glAttachShader(_program, _fragShader);
}

_hashForUniforms = nullptr;

CHECK_GL_ERROR_DEBUG();


return true;
}

我们回来看初始化的代码,这段代码包含如下:

  • 着色器程序的创建(program)
  • 顶点着色器和片段着色器的编译和绑定
  • 每个阶段的错误检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
GLint status;

if (!source) {
return false;
}

const GLchar *sources[] = {
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
(type == GL_VERTEX_SHADER ? "precision highp float;\n" : "precision mediump float;\n"),
#endif
"uniform mat4 CC_PMatrix;\n"
"uniform mat4 CC_MVMatrix;\n"
"uniform mat4 CC_MVPMatrix;\n"
"uniform vec4 CC_Time;\n"
"uniform vec4 CC_SinTime;\n"
"uniform vec4 CC_CosTime;\n"
"uniform vec4 CC_Random01;\n"
"//CC INCLUDES END\n\n",
source,
};

*shader = glCreateShader(type);
glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);
glCompileShader(*shader);

glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);

if (! status) {
GLsizei length;
glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);
GLchar* src = (GLchar *)malloc(sizeof(GLchar) * length);

glGetShaderSource(*shader, length, nullptr, src);
CCLOG("cocos2d: ERROR: Failed to compile shader:\n%s", src);

if (type == GL_VERTEX_SHADER) {
CCLOG("cocos2d: %s", getVertexShaderLog().c_str());
} else {
CCLOG("cocos2d: %s", getFragmentShaderLog().c_str());
}

free(src);

abort();
}

return (status == GL_TRUE);
}

然后我们再来看看这个封装的编译着色器的函数,这里值得注意的是cocos2dx添加的统一值。

  • uniform mat4 CC_PMatrix;
  • uniform mat4 CC_MVMatrix;
  • uniform mat4 CC_MVPMatrix;
  • uniform vec4 CC_Time;
  • uniform vec4 CC_SinTime;
  • uniform vec4 CC_CosTime;
  • uniform vec4 CC_Random01;

链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool GLProgram::link()
{
CCASSERT(_program != 0, "Cannot link invalid program");

GLint status = GL_TRUE;

glLinkProgram(_program);

if (_vertShader) {
glDeleteShader(_vertShader);
}

if (_fragShader) {
glDeleteShader(_fragShader);
}

_vertShader = _fragShader = 0;

return (status == GL_TRUE);
}

然后在设置完顶点属性和统一值等等东西之后做了这些事情:

  • 先将着色器程序连接到openGL之中
  • 然后清理编译出来的顶点和片段着色器