我已经尽力了解OpenGL的功能,但我对glShaderSource
的参数存在问题:
void glShaderSource(
GLuint shader,
GLsizei count,
const GLchar * const * string,
const GLint * length);
我对最后两个参数感到困惑。它们在C ++中实际意味着什么?这是否意味着我给它一个字符串,一个const char或一个字符串的指针,如果那可能的话?为什么?
答案 0 :(得分:24)
glShaderSource
需要两个相关的序列(C样式数组)。
第一个序列是一个C字符串数组,无论是否为零终止。
第二个序列是一个整数数组,表示第一个序列中每个字符串的长度。如果字符串是零终止的,则此序列是可选的,因为库将自己找到长度。
GL-prefixed类型是因为OpenGL规范需要讨论类型而不将其自身绑定到特定语言,因此它引入了常见C类型的别名。
GLchar
是类似于C char
的类型,用于表示狭义字符。
GLint
是有符号整数类型,通常用于表示对象句柄和偏移量。
还有其他人,例如GLenum
和GLvoid
。
GLchar const*
是char const*
类型的OpenGL拼写。除了用于指向单个字符外,它通常用于表示一串字符。当以该含义使用时,它应指向一个字符序列,以一个标记值'\0'
结束,以便知道字符串结束。
使glShaderSource
占用多个字符串的原因是因为OpenGL的着色器编译器暴露了文件的概念。这些字符串中的每一个都代表一个文件的内容,它将进行编译,就好像这些文件连接在一起一样。请注意,此文件在很大程度上与同名的文件系统无关。 glShaderSource
仅处理包含文字的字符串。
当您想要将一些片段组合到完整的着色器源中时,这是有益的,例如,如果您想要添加#version
语句或一些常见的前导码,或者已经实现了某种包含指令自己。
关于如何使用它的一个例子:
std::string v = "#version 150\n";
std::string c = ReadTextFile("common.glsl"); // read a string with the file contents
std::string s = ReadTextFile("mesh.vert"); // same here
GLchar const* files[] = { v.c_str(), c.c_str(), s.c_str() };
GLint lengths[] = { v.size(), c.size(), s.size() };
glShaderSource(id, 3, files, lengths);
这里我们将OpenGL的三个字符串组合起来考虑作为一大块源文本。请注意,我的便捷函数ReadTextFile
将文件系统文件的内容读入字符串,OpenGL的任何部分都没有触及文件系统。如果要将文本文件,图像文件或网格文件的内容导入OpenGL结构,则需要提前自己阅读。
答案 1 :(得分:11)
让我们一步一步地完成这个步骤:
const GLchar
是一个不可变(常量)字符。const GLchar *
是指向不可变GLchar
的指针,在本例中是您的着色器源。const GLchar * const
是指向不可变GLchar
的不可变指针,这意味着指针本身无法更改为指向其他位置。const GLchar * const *
是指向不可变GLchar
的不可变指针的指针。string
只是参数的名称。这意味着它需要一个指向常量GLchar
的常量指针的指针。您可以像这样使用它(对于大小使用NULL,让glShaderSource
计算出长度):
const GLchar *source = "my awesome shader";
glShaderSource(myShader, 1, &source, NULL);
或者指定多个来源:
const GLchar *sources[] = {
"my awesome shader",
"another awesome"
};
glShaderSource(myShader, sizeof(sources)/sizeof(*sources), sources, NULL);
我实际上没有测试过上面的代码,可能会有一些必要的演员表,但它应该原则上说明它是如何工作的。
答案 2 :(得分:0)
我想知道如何将(着色器)文件读入字符串以传递给 glShaderSource()
。但这甚至没有必要。
OpenGL 文档 (khronos.org) 说:
<块引用>如果 length 为 NULL,则假定每个字符串都以 null 结尾。
来自以下内容:“字符串”确实不必须空终止,但是您必须传递正确的长度。
该命令允许您传递多个字符串/长度对 - 即 count
参数。拉斯的回答有一个很好的例子。
这是将着色器文件读入未终止字符串的函数体。它是用 C 语言编写的,非常简单直接,没有任何复制,只有一个 open()
、fstat()
和 mmap()
。只需要稍微摆弄类型,因为我有 count=1
并且没有额外的数组变量。
int fd = open(filename, O_RDONLY);
if (fd == -1) {
printf("Error opening shader file %s", filename);
exit(EXIT_FAILURE);
}
通常我不太在意错误,但文件名总是错误的,而且下面的命令不会喜欢 -1 的 fd 或其结果......
struct stat stb;
fstat(fd, &stb);
这是获取长度(字符串长度=文件大小)。
const char *mm = mmap(NULL, stb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
mm
现在指向字符串的开头。 mm
是 字符串,句点。但是结尾没有定义(?),我们只知道长度。 mm[stb.st_size] = '\0'
不起作用,因为...不同的原因。
close(fd);
可以在 mmap()
之后完成。
我没有将 mm
(地址)分配到一个单一大小的数组中,而是传递 &mm
:
glShaderSource(shader, 1, &mm, (int *)&stb.st_size);
length/st_size 类似;只有这里的值在结构内。看起来有点吓人。这就是 fstat()
和 glShaderSource()
碰撞的方式。和 gcc 编译器:您实际上可以以编译器警告为代价省略 (int *)
强制转换。
也可以使用 void *mm = mmap(...)
,但会产生(有指导意义的)警告。
编译器同时需要:&
和正确的类型。至少可以省略第二个 const
。
即使将 (int *)&stb.st_size)
替换为 NULL
和 hoping 在我的情况下也适用于空终止。可能需要正好 4096 字节的着色器文件来引发错误。或者其他某种(在这种情况下)当之无愧的厄运。
我忽略了这一点 - 在 glShaderSource()
从 mm
复制之后,您可以:
munmap((void *)mm, stb.st_size);
所以正确类型的 mm
并没有真正得到回报,因为它必须被重铸以防止出现警告......也许只是让它void *mm
并执行整个 数组-of-(pointer-to)-带有函数参数的作业。
glShaderSource(shader, 1, (const char **)&mm, (int *)&stb.st_size)
另一方面,由于有两个额外的数组和四个额外的行,大多数演员表、星号和与符号都消失了:
const char *mm_arr[1]; // array of one (string aka char pointer)
mm_arr[0] = mm; // first and only element is set; no cast needed even if 'void *mm'
int sz_arr[1]; // array of a single int
sz_arr[0] = stb.st_size; // normal assignment of int to int
glShaderSource(shader, 1, mm_arr, sz_arr);
为了灵活性 - 好像他们认为(很久以前!):这些程序员很可能用 getline() 左右读取他们的着色器文件,所以让我们不要强迫他们合并行和/或 null 终止它们。他们可以直接通过。
不是指定多个着色器(如另一个 A 建议的那样),而是指定一个着色器在多个部分(线条或逻辑部分)中。
这是我发现的一个 C++ 示例,它的功能完全相同(但使用空终止,而不是大小):
fstram.open(shaderPath);
sstream << fstram.rdbuf();
fstram.close();
id = glCreateShader(type);
auto data = sstream.str();
const char* dataPtr = data.c_str();
glShaderSource(id, 1, &dataPtr, NULL);
有 fstram.rdbuf()
和 sstream.str()
和 data.c_str()
。这实际上并没有简单得多。一种或另一种方式,在您可以传递该 &dataPtr
伪数组之前,有一些运行。
(dataPtr
这个名字非常...防御性)
官方规格:
<块引用>const GLchar **string
字符串
指定指向字符串s 的指针数组,其中包含要加载到着色器中的源代码。
我更喜欢:
strings -- 指定一个字符指针数组。这些字符串包含...
这可能看起来微不足道,但是在将整个文件“啜饮”成一个字符串之后,您会想知道这个复数、数组、指针或与符号。
我只看到 slang/vstest.c
中的 mesa-demos
使用大小:
f = fopen (filename, "r");
if (f == NULL)
return;
fseek (f, 0, SEEK_END);
size = ftell (f);
if (size == -1) {
fclose (f);
return;
}
fseek (f, 0, SEEK_SET);
code = (char *) (malloc (size));
if (code == NULL) {
fclose (f);
return;
}
size = fread (code, 1, size, f);
fclose (f);
glShaderSourceARB (vert, 1, (const GLcharARB **) (&code), &size);
我更喜欢fstat()
和mmap()
以及(const char **)&code
。