参数对glShaderSource的含义是什么?

What is the meaning of the parameters to glShaderSource?

本文关键字:是什么 glShaderSource 参数      更新时间:2023-10-16

我已经尽力深入理解OpenGL的功能,但我对glShaderSource的参数有问题:

void glShaderSource(
GLuint shader,
GLsizei count,
const GLchar * const * string,
const GLint * length);

我对最后两个参数感到困惑。它们在C++中的实际含义是什么?这是否意味着我给它一个字符串,一个常量字符或字符串的指针(如果可能的话)?为什么呢?

glShaderSource需要两个相关的值序列(C 样式数组)。

第一个序列是 C 字符串数组,要么以零结尾,要么不终止。

第二个序列是一个整数数组,指示第一个序列中每个字符串的长度。如果字符串以零结尾,则此序列是可选的,因为库将自行查找长度。

GL 前缀类型是因为 OpenGL 规范需要讨论类型而不将自己绑定到特定语言中,因此它引入了常见 C 类型的别名。

GLchar是一种类似于Cchar的类型,用于表示狭义字符。GLint是有符号整数类型,通常用于表示对象句柄和偏移量。 还有其他像GLenumGLvoid这样的。

GLchar const*char const*类型的OpenGL拼写。除了用于指向单个字符外,它还通常用于表示一串字符。当用于该含义时,它应指向一系列字符,以哨点值结尾,''知道字符串结束。

之所以glShaderSource采用多个字符串,是因为OpenGL的着色器编译器公开了文件的概念。这些字符串中的每一个都表示一个文件的内容,它将像这些文件连接在一起一样进行编译。请注意,此文件在很大程度上与同名的文件系统无关。glShaderSource仅处理包含文本的字符串。

当您想要将一些片段组合到完整的着色器源中时,例如,如果要在#version语句或一些常见的前导码前面加上前缀,或者自己实现了某种包含指令,这非常有用。

至于如何使用它的示例:

std::string v = "#version 150n";
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 结构,则需要提前自行读取。

让我们一步一步地完成:

  • const GLchar是不可变(常量)字符。
  • const GLchar *是指向不可变GLchar的指针,在本例中为着色器源。
  • const GLchar * const是指向不可变GLchars 的不可变指针,这意味着指针本身不能更改为指向其他地方。
  • const GLchar * const *是指向不可变GLchar的不可变指针的指针。
  • 最后string只是参数的名称。

这意味着它需要一个指向常量指针的指针,指向常量GLchars。你可以这样使用它(使用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);

我还没有实际测试上面的代码,可能需要一些强制转换,但它应该显示它原则上是如何工作的。

我想知道如何将(着色器)文件读取为字符串以传递给glShaderSource()。但这甚至没有必要。

OpenGL文档(khronos.org)说:

如果长度为 NULL,则假定每个字符串都以 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] = ''不起作用,因为...不同的原因。

<小时 />
close(fd);

mmap()后可以完成。


我没有将mm(地址)分配给一个大小的数组,而是传递&mm

glShaderSource(shader, 1, &mm, (int *)&stb.st_size);

长度/st_size类似;只有这里的值在结构内。看起来有点吓人。这就是fstat()glShaderSource()碰撞的方式。和 gcc 编译器:您实际上可以省略(int *)转换,但代价是编译器警告。

void *mm = mmap(...)也有效,但会产生(指导性)警告。

编译器需要两者:&和正确的类型。至少可以忽略第二个const

即使在我的情况下,甚至用NULL替换(int *)&stb.st_size)并希望空终止也有效。可能需要正好 4096 字节的着色器文件才能引发错误。或者其他一些(在这种情况下)当之无愧的坏运气。


这个我忽略了 - 在glShaderSource()mm复制后,您可以:

munmap((void *)mm, stb.st_size);

因此,正确的mm类型并没有真正得到回报,因为它必须重新转换以防止警告......也许只是把它留void *mm并使用函数参数完成整个(指针指向)数组的工作。

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的指针数组,其中包含要加载到着色器的源代码。

我更喜欢:

字符串 -- 指定字符指针数组。这些字符串包含...

这可能看起来微不足道,但是在将整个文件"咕噜咕噜"成一个字符串之后,您想知道这个复数,或数组,或指针,或与号。


我只看到使用尺寸mesa-demosslang/vstest.c

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.