这篇我们来介绍函数部分,也就是下面这些:
static unsigned int CompileShader(unsigned int type,const std::string& source)
{
unsigned int id = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result==GL_FALSE)
{
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile"<<
(type==GL_VERTEX_SHADER?"vertex":"fragment")
<< "shader!" << std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
下面来逐步解释每一行代码:
static unsigned int CompileShader(unsigned int type,const std::string& source)
static
:这个关键字表示函数是静态的,意味着它属于类或命名空间,而不是特定的对象实例。它可以通过类名或命名空间名直接调用,而无需创建对象。
unsigned int type
:这是一个参数,表示要编译的着色器的类型。可以是 GL_VERTEX_SHADER
(顶点着色器)或 GL_FRAGMENT_SHADER
(片段着色器)。
const std::string& source
:这是另一个参数,表示要编译的着色器源代码。它是一个引用,指向一个字符串对象,其中包含了要编译的着色器源代码。
unsigned int id = glCreateShader(type);
这行代码的作用是使用 glCreateShader
函数创建一个着色器对象,并将返回的标识符(ID)存储在名为 id
的无符号整数变量中。
unsigned int id
:这是一个无符号整数变量,用于存储着色器对象的标识符(ID)。在OpenGL中,标识符通常是无符号整数类型。
glCreateShader(type)
:这是一个OpenGL函数调用,用于创建一个指定类型的着色器对象。type
参数表示着色器的类型,可以是 GL_VERTEX_SHADER
(顶点着色器)或 GL_FRAGMENT_SHADER
(片段着色器)。函数返回一个表示新创建着色器对象的唯一标识符。
const char* src = source.c_str();
这行代码的作用是将从 source
字符串对象获取的C风格字符串(null-terminated string)赋值给名为 src
的 const char*
指针。
const char* src
:这是一个指向字符的指针,它被声明为指向常量字符(const char
),表示它指向的字符数据是只读的。
source.c_str()
:这是一个 std::string
对象的成员函数,用于返回一个指向以null结尾的C风格字符串的指针。通过调用这个函数,你可以获取 source
字符串对象的C风格表示。
这个操作通常用于将C++的 std::string
类型转换为C风格字符串,因为很多OpenGL函数需要接受C风格字符串作为参数。在这种情况下,从 source
字符串对象中获取一个C风格字符串指针,以便在后续的OpenGL函数中使用。请注意,因为 src
是一个指向常量字符的指针,所以不能通过它来修改原始字符串数据。
glShaderSource(id, 1, &src, nullptr);
id
:这是一个无符号整数(GLuint
),代表着色器对象的标识符。你可以通过 glCreateShader
函数创建着色器对象,并将其标识符赋给 id
。
1
:这是一个整数,表示要加载的源代码字符串的数量。在这里,我们加载一个源代码字符串。
&src
:这是一个指向源代码字符串的指针。由于在之前的代码中,我们将 source
字符串对象的C风格表示(即以null结尾的字符序列)赋给了 src
指针,所以可以通过 &src
获取该指针的地址。
nullptr
:这是一个指向字符数组的长度的指针。在这里,我们没有指定字符串长度,因为OpenGL会根据null终止符自动计算字符串长度。
总的来说,glShaderSource
函数的作用是将源代码加载到着色器对象中,使着色器对象拥有这些源代码,以便后续的编译操作。
这个函数比较复杂,我来解释一下,
函数签名:
void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
shader
:表示要设置源代码的着色器对象的标识符。这是通过 glCreateShader
创建的。count
:表示源代码字符串的数量。通常情况下,你会加载一个源代码字符串,所以值为 1。string
:是一个指向源代码字符串的指针的指针(const GLchar**
)。这里的 string
是一个数组,每个元素指向一个源代码字符串的起始位置。你可以使用单个源代码字符串,也可以将多个源代码字符串合并成一个数组。length
:是一个指向整数数组的指针(const GLint*
),用于指定每个源代码字符串的长度。通常情况下,你可以将其设置为 nullptr
,OpenGL会自动计算字符串长度。glShaderSource
函数将源代码加载到指定的着色器对象中,以便后续使用 glCompileShader
函数对其进行编译。
glCompileShader(id);
调用 glCompileShader
函数会编译着色器对象中的源代码,将其转换为可以在图形渲染管线中执行的机器代码。如果源代码中存在语法错误、逻辑问题或其他编译错误,编译过程将失败,并且你可以通过查询编译状态和错误日志来调试和查找问题。
一旦着色器成功编译,你就可以将其与其他着色器链接到一起,或将其用于渲染管线的其他阶段,从而实现所需的图形渲染效果。
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
glGetShaderiv
是一个OpenGL函数,用于查询着色器对象的特定信息。在这段代码中,它用于查询着色器的编译状态,即是否成功编译了着色器源代码。
函数签名:
void glGetShaderiv(GLuint shader, GLenum pname, GLint* params);
参数解释:
shader
:表示要查询的着色器对象的标识符。pname
:表示要查询的参数名称,这里是 GL_COMPILE_STATUS
,用于查询编译状态。params
:是一个指向整数的指针,用于存储查询结果。在这段代码中,传递的 pname
是 GL_COMPILE_STATUS
,表示想查询编译状态。params
是一个指向 result
的指针,用于存储查询结果。如果编译成功,result
将被设置为 GL_TRUE
,如果编译失败,result
将被设置为 GL_FALSE
。
通过查询编译状态,你可以判断着色器是否成功编译,从而在发生编译错误时进行适当的处理和调试。
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
在这段代码中,glGetShaderiv
函数用于查询着色器信息日志的长度。这个信息日志包括着色器编译过程中的警告、错误和其他相关信息。通过查询信息日志的长度,你可以了解信息日志所需的缓冲区大小,然后可以为信息日志分配足够的空间来存储这些信息。
具体地说,以下是这段代码中各个参数的含义:
id
:着色器对象的标识符,即你之前通过 glCreateShader
创建的着色器对象。GL_INFO_LOG_LENGTH
:表示你要查询的参数名称,这里是用于查询信息日志长度的参数。&length
:一个指向整数的指针,用于存储查询结果,即信息日志的长度。通过这个查询,你可以获得信息日志的长度,然后根据这个长度分配足够大的内存,以便在之后的调用中使用 glGetShaderInfoLog
函数获取实际的信息日志内容。这些信息日志对于调试和定位着色器编译过程中的问题非常有用。
char* message = (char*)alloca(length * sizeof(char));
alloca
:alloca
是一个函数,用于在栈上动态分配内存空间。与标准的动态内存分配函数(如 malloc
)不同,alloca
分配的内存在函数退出时会自动释放,因为它是在栈上分配的。这意味着分配的内存空间不能在函数外部访问,也不能在函数内部的其他函数中访问。在这里,它用于为信息日志分配临时的内存空间。
char* message
:这声明了一个指向字符的指针,名为 message
,用于存储信息日志内容。
(char*)alloca(length * sizeof(char))
:这部分是对 alloca
函数的调用。alloca
分配的内存大小由 length * sizeof(char)
决定,其中 length
是通过查询 GL_INFO_LOG_LENGTH
得到的信息日志长度。sizeof(char)
是一个字符的字节数(通常为1),因此这里的乘法实际上只是为了分配与信息日志长度相等的内存空间。类型转换 (char*)
是将 alloca
返回的指针转换为指向字符的指针,以便后续存储字符串数据。
总之,这段代码的目的是在栈上分配一块内存空间,以便在之后的调用中存储着色器信息日志的内容。需要注意的是,由于 alloca
分配的内存在函数退出时会自动释放,所以你在退出函数后不能再使用指向这块内存的指针。
glGetShaderInfoLog(id, length, &length, message);
id
:着色器对象的标识符,即你之前通过 glCreateShader
创建的着色器对象。length
:这是之前查询到的信息日志的长度,通过调用 glGetShaderiv
传递 GL_INFO_LOG_LENGTH
参数获得的。&length
:这是一个指向整数的指针,传递给 glGetShaderInfoLog
函数,用于接收实际写入信息日志内容的字符数。message
:这是一个之前通过 alloca
分配的字符数组,用于存储信息日志的内容。通过调用 glGetShaderInfoLog
函数,OpenGL将实际的信息日志内容写入到 message
数组中,并将实际写入的字符数更新到 length
变量中。你可以随后使用这些内容来查看着色器编译过程中的警告、错误和其他消息,以帮助你进行调试和排错。
return id;
在函数 CompileShader
的最后,有一行代码 return id;
,它的作用是将编译后的着色器对象标识符返回给调用者。这样做的目的是让调用者可以在后续的代码中继续使用这个着色器对象,例如将它附加到程序对象中,以便进行渲染。
在这个特定的代码中,CompileShader
函数负责编译着色器并返回一个表示着色器对象的标识符。调用者(可能是 CreateShader
函数)将得到这个标识符,并在适当的地方使用它。通过返回标识符,你可以在多个函数之间传递着色器对象,并在不同的地方使用它,以便实现更复杂的OpenGL操作。