参数对glShaderSource的含义是什么?
What is the meaning of the parameters to glShaderSource?
我已经尽力深入理解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
是有符号整数类型,通常用于表示对象句柄和偏移量。 还有其他像GLenum
和GLvoid
这样的。
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
是指向不可变GLchar
s 的不可变指针,这意味着指针本身不能更改为指向其他地方。const GLchar * const *
是指向不可变GLchar
的不可变指针的指针。- 最后
string
只是参数的名称。
这意味着它需要一个指向常量指针的指针,指向常量GLchar
s。你可以这样使用它(使用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-demos
slang/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
.
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- C++中名称篡改的目的是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 是什么阻止DOMTimerCoordinator::NextID进入无休止的循环
- 派生类销毁的最佳实践是什么
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 通过JNI传递数据数组的最快方法是什么
- "using namespace std;"在C++的作用是什么?
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 文件系统:复制功能的速度秘诀是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么