通过glUniform将GLM的向量类型传递到OpenGL

4

背景

我正在用C++编写一个OpenGL glUniform 函数的包装器,旨在使它们类型安全。我有许多 set_uniform 函数,可以重载以接受 OpenGL PODs(GLintGLuintGLfloat)或任何 GLM 向量和矩阵类型。

我认为到目前为止一切都很简单,但是我遇到了布尔类型的问题。GLSL 提供了 boolbec2bvec3bvec4,因此我必须提供一个 set_uniform 重载以支持 GLboolean 和 GLM 布尔向量类型。

根据OpenGL手册,没有接受GLboolean或指向GLboolean数组的glUniform函数。我必须传递GLint、GLuint或GLfloat,驱动程序将为我执行转换。
可以使用i、ui或f变体来为bool、bvec2、bvec3、bvec4或这些类型的数组提供统一变量的值。如果输入值为0或0.0f,则统一变量将设置为false,否则将设置为true。
在传递之前将GLboolean转换为GLint很容易,但是GLM向量类型证明更加困难。我深入实现,对该库越来越担心。
问题
向OpenGL传递GLM向量类型的推荐方法是使用glm::value_ptr:
glm::bvec3 v(true, true, false);
glUniform3iv(some_uniform_id, 1, glm::value_ptr(v));

我对这段代码有很多问题。
首先,glm::bvec3被实现为一个由3个bool组成的结构体(不是GLboolean,而是C++的bool)。我认为我不应该直接传递它,因为glUniform3iv期望一个指向一些GLintvoid指针。C++规范对bool的大小没有任何保证。这意味着glUniform3iv可能会读取第二个和第三个组件的垃圾值,或者更糟的是,它实际上正在读取数组末尾之后的内容。
为了纠正这个问题,我在传递给OpenGL之前将glm::bvec3转换为glm::ivec3
glm::bvec3 bv(true, true, false);
glm::ivec3 iv = bv;
glUniform3iv(some_uniform_id, 1, glm::value_ptr(iv));

我对此并不完全满意,因为glm::ivec3的值类型是glm::detail::mediump_int_t,它是一个typedef,代表int而不是GLint,但也许可以归结为“库设计人员知道大小相同”的原因。

第二个更重要的问题是glm::value_ptr仅传递第一个struct成员的地址,并将struct视为没有考虑填充的array

这里有什么我没注意到的吗?GLM库与OpenGL一起被广泛使用,甚至在Khronos自己的维基上都有列出。然而,它提供给OpenGL的传递结构的函数,即glm::value_ptr,没有努力确保它传递的类型实际上与OpenGL期望的类型大小相同,完全忽略可能存在的任何填充。GLM库是否在类型大小和结构填充方面做了一些隐藏的技巧,以使发送到OpenGL的数据有效,还是该库存在一些严重的基本问题?

1个回答

4
“GLM库是否在类型大小和结构填充方面做了一些隐藏的技巧,以使发送到OpenGL的数据有效,还是这个库有一些严重的基本问题?” “都不是。它只是像其他人一样对于结构布局和指针算术行为做出相同的假设。” “C++标准不允许'value_ptr'工作;这显然是未定义的行为。但这也是处理此类事情的常用技术。很多真实的、功能性的代码都假设一个'struct { int x; int y;};'可以被认为等价于'int[2]'。在大多数C++实现下,这将按预期运行。” “当涉及低级编程时,做出这种假设并不是不合理的。”
我并不完全满意这个方案,因为glm::ivec3的值类型是glm::detail::mediump_int_t,这是一个typedef,指向int而不是GLint,但也许可以归结为“库设计者知道大小是相同的”。
这与此无关。虽然GLM被称为“OpenGL Mathematics”,但它本身并不依赖于OpenGL。因此,它没有访问GLint或任何其他OpenGL定义的类型的权限。
因此,您可以假设ivec3value_type将与GLint相同(您甚至可以编写static_assert来验证它),或者您可以创建自己的变体。毕竟,GLM是模板化的:
using gl_ivec3 = glm::tvec<GLint, 3>;
...
glm::gl_ivec3 iv = bv;
glUniform3iv(some_uniform_id, 1, glm::value_ptr(iv));

1
因此,value_ptr的实现基本上遵循了一种事实标准,即使它不是100%可移植的。尽管这与GLM网站上的说法相矛盾,即它是一个“平台无关库”,并支持“任何符合C ++ 98或C ++ 11编译器”的说法,但这是有道理的。我会按照您的建议使用OGL值类型对GLM结构体进行typedef。为了与OGL互操作而制作库却不提供OGL所期望的数据格式,这是疯狂的。没有任何依赖关系。C ++提供了大小类型,并且OGL所需的大小很容易获得 - Fibbs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接