编译着色器程序
由于着色器是在GPU中执行,因此想要使用写好的着色器,必须使用特定的编译器来解析数据,编译代码,生成可执行程序。在OpenGL调用相关的API就可以实现。
流程如下:
相关API如下
GLuint glCreateShader(GLenum type) 功 能:创建着色器对象 返回值:一个非0整数,如果是0则说明发送了错误 参 数:顶点着色器对应GL_VERTEX_SHADER,其他各着色阶段对应不同的参数 |
void glShaderSource(GLuint shader, GLsizei count, const GLchar* *string, const GLint *length) 功 能:将着色器源码关联到着色器对象 返回值:void 参 数:shader 着色器对象 count 个数 string 着色器源码 length NULL |
void glCompileShader(GLuint shader) 功 能:编译着色器 返回值:void 参 数:shader 着色器对象 |
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) 功 能:错误检查 返回值:void 参 数:shader 着色器对象 pname GL_COMPILE_STATUS params 输出参数,判断是否编译错误,如果是个非零值则编译成功。 |
void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) 功 能:打印编译错误信息 返回值:void 参 数:shader 着色器对象 bufSize buffer的大小 length NULL infoLog 返回的错误信息 |
GLuint glCreateProgram(void) 功 能:创建着色器程序 返回值:返回一个着色器程序ID 参 数:void |
void glAttachShader(GLuint program, GLuint shader) 功 能:把着色器对象附件到着色器程序 返回值:void 参 数:program 着色器程序 shader 着色器对象 |
void glLinkProgram(GLuint program) 功 能:链接到着色器程序 返回值:void 参 数:program 着色器程序 |
void glGetProgramiv(GLuint program, GLenum pname, GLint *params) 功 能:着色器程序错误检查 返回值:void 参 数:program 着色器程序ID pname GL_LINK_STATUS params 输出参数,如果是个非零值则成功。 |
void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)功 能:打印错误信息 返回值:void 参 数:shader着色器对象 bufSize buffer的大小 length NULL infoLog 输出参数,返回的错误信息 |
实现:
/加载着色器程序
/*
* id 返回参数,返回着色器ID
* vertexPath 顶点着色器
* fragmentPath 片元着色器
* geometryPath 几何着色器
*/
void QOPenWidget::CompileShader(unsigned int *id, const char* vertexPath, const char* fragmentPath, const char* geometryPath)
{
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
// 打开文件
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// 读取文件的缓冲内容到数据流中
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// 关闭文件处理器
vShaderFile.close();
fShaderFile.close();
// 转换数据流到string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
if (geometryPath != NULL)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. 编译着色器
unsigned int vertex, fragment;
int success;
char infoLog[512];
// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
//编译着色器
glCompileShader(vertex);
//检测编译时错误
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
//如果编译错误进入语句
if (!success)
{
//获取错误信息
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
//打印错误
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//片元着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
//检测错误
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
//获取错误原因
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
//打印
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//几何着色器
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
}
// 着色器程序
*id = glCreateProgram();
glAttachShader(*id, vertex);
glAttachShader(*id, fragment);
if (geometryPath != nullptr)
glAttachShader(*id, geometry);
glLinkProgram(*id);
//检测错误
glGetProgramiv(*id, GL_LINK_STATUS, &success);
if (!success)
{
//获取错误信息
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
参见:《OpenGL编程指南》第八版第2章