**着色器程序对象(Shader Program Object)**是多个着色器合并之后并最终链接完成的版本。如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。
当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入。当输出和输入不匹配的时候,你会得到一个连接错误。
unsigned int shaderProgram; // 着色器程序
shaderProgram = glCreateProgram(); // 创建着色器程序 glCreateProgram: 创建着色器程序
glCreateProgram函数创建一个程序,并返回新创建程序对象的ID引用。现在我们需要把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们:
glAttachShader(shaderProgram, vertexShader); // 附加顶点着色器 shaderProgram: 着色器程序 vertexShader: 顶点着色器 glAttachShader: 附加着色器
glAttachShader(shaderProgram, fragmentShader); // 附加片段着色器 shaderProgram: 着色器程序 fragmentShader: 片段着色器 glAttachShader: 附加着色器
glLinkProgram(shaderProgram); // 链接着色器程序 shaderProgram: 着色器程序 glLinkProgram: 链接着色器程序
就像着色器的编译一样,我们也可以检测链接着色器程序是否失败,并获取相应的日志。与着色器编译不同,我们不会调用glGetShaderiv和glGetShaderInfoLog,现在我们使用:
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); // 获取着色器程序链接状态 shaderProgram: 着色器程序 GL_LINK_STATUS: 链接状态 &success: 状态值
if(!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); // 获取着色器程序链接信息 shaderProgram: 着色器程序 512: 信息长度 NULL: 信息长度 infoLog: 信息
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; // 输出错误信息
}
得到的结果就是一个程序对象,我们可以调用glUseProgram函数,用刚创建的程序对象作为它的参数,以激活这个程序对象:
glUseProgram(shaderProgram); // 使用着色器程序 shaderProgram: 着色器程序 glUseProgram: 使用着色器程序
在glUseProgram函数调用之后,每个着色器调用和渲染调用都会使用这个程序对象(也就是之前写的着色器)了。
对了,在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们了:
glDeleteShader(vertexShader); // 删除顶点着色器 vertexShader: 顶点着色器 glDeleteShader: 删除着色器
glDeleteShader(fragmentShader); // 删除片段着色器 fragmentShader: 片段着色器 glDeleteShader: 删除着色器
现在,我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。就快要完成了,但还没结束,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。我们需要告诉OpenGL怎么做。