glShaderSource参数的含义是什么?

时间:2014-02-28 13:38:13

标签: opengl c++

我已经尽力了解OpenGL的功能,但我对glShaderSource的参数存在问题:

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

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

3 个答案:

答案 0 :(得分:24)

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

第一个序列是一个C字符串数组,无论是否为零终止。

第二个序列是一个整数数组,表示第一个序列中每个字符串的长度。如果字符串是零终止的,则此序列是可选的,因为库将自己找到长度。

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

GLchar是类似于C char的类型,用于表示狭义字符。 GLint是有符号整数类型,通常用于表示对象句柄和偏移量。 还有其他人,例如GLenumGLvoid

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) 替换为 NULLhoping 在我的情况下也适用于空终止。可能需要正好 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