我应该在统一的缓冲区或着色器存储缓冲区对象内使用“vec3”吗?
vec3
types是一个非常好的types。 它只需要3个浮点数,而我有数据只需要3个浮点数。 我想在UBO和/或SSBO的结构中使用一个:
layout(std140) uniform UBO { vec4 data1; vec3 data2; float data3; }; layout(std430) buffer SSBO { vec4 data1; vec3 data2; float data3; };
然后,在我的C或C ++代码中,我可以这样做来创build匹配的数据结构:
struct UBO { vector4 data1; vector3 data2; float data3; }; struct SSBO { vector4 data1; vector3 data2; float data3; };
这是一个好主意吗?
没有! 永远不要这样做!
声明UBO / SSBO时,假设所有3元素vector和matrixtypes都不存在 。 假设唯一的types是标量,2和4个元素向量(和matrix)。 如果你这样做的话,你会为自己节省很多的痛苦。
如果你想要一个vec3 +浮动的效果,那么你应该手动打包:
layout(std140) uniform UBO { vec4 data1; vec4 data2and3; };
是的,你将不得不使用data2and3.w
来获得另一个值。 处理它。
如果你想要vec3
的数组,然后让他们vec4
的数组。 使用3元素向量的matrix也是如此。 只要从SSBOs / UBOs中消除3元素载体的全部概念, 从长远来看,你会好得多。
有两个原因你应该避免vec3
:
它不会做什么C / C ++
如果使用std140
布局,那么您可能需要定义C或C ++中与GLSL中的定义相匹配的数据结构。 这使得两者之间很容易混搭。 在大多数情况下, std140
布局至less可以做到这一点。 但是当涉及到vec3
时,其布局规则与C和C ++编译器通常的布局规则不匹配。
考虑一下vec3
types的以下C ++定义:
struct vec3a { float a[3]; }; struct vec3f { float x, y, z; };
这两个都是完全合法的types。 这些types的大小和布局将匹配std140
所需的大小和布局。 但它不符合std140
强制的alignment行为。
考虑这个:
//GLSL layout(std140) uniform Block { vec3 a; vec3 b; } block; //C++ struct Block_a { vec3a a; vec3a b; }; struct Block_f { vec3f a; vec3f b; };
在大多数C ++编译器中, Block_a
和Block_b
sizeof
将是24.这意味着b
Block_b
将是12。
然而,在std140布局中, vec3
总是alignment到4个字。 因此Block.b
的偏移量为16。
现在,您可以尝试使用C ++ 11的alignas
function(或C11的类似_Alignas
function)来解决这个问题:
struct alignas(16) vec3a_16 { float a[3]; }; struct alignas(16) vec3f_16 { float x, y, z; }; struct Block_a { vec3a_16 a; vec3a_16 b; }; struct Block_f { vec3f_16 a; vec3f_16 b; };
如果编译器支持16字节alignment,这将工作。 或者至less,它将在Block_a
和Block_f
的情况下工作。
但在这种情况下不起作用:
//GLSL layout(std140) Block2 { vec3 a; float b; } block2; //C++ struct Block2_a { vec3a_16 a; float b; }; struct Block2_f { vec3f_16 a; float b; };
按照std140
的规则,每个vec3
必须以16字节的边界开始。 但vec3
不消耗 16个字节的存储空间; 它只消耗12.由于float
可以在4字节的边界上开始,所以vec3
后面跟着一个float
将占用16个字节。
但是C ++alignment的规则不允许这样的事情。 如果某个types与X字节边界alignment,那么使用该types将消耗X字节的倍数。
所以匹配std140
的布局要求你根据使用的地方select一个types。 如果后面跟着一个float
,则必须使用vec3a
; 如果后面跟着一个大于4字节的types,那么你必须使用vec3a_16
。
或者你可以在着色器中不使用vec3
,避免所有这些增加了复杂性。
请注意,基于alignas(8)
的vec2
不会有这个问题。 C / C ++也不会使用正确的alignment说明符来构造&数组(尽pipe数组较小的types有它们自己的问题)。 这个问题只发生在使用裸vec3
。
实施支持是模糊的
即使你做的一切都是正确的,实现已经被错误地实现了vec3
的古怪的布局规则。 一些实现有效地将C ++alignment规则强加给GLSL。 所以,如果你使用vec3
,它会像C ++一样对待一个16字节的alignmenttypes。 在这些实现中,一个vec3
后跟一个float
将像vec4
跟随一个float
。
是的,这是实施者的错。 但是由于您无法修复实施,您必须解决此问题。 而最合理的做法是完全避免vec3
。
请注意,对于Vulkan,SDK的GLSL编译器可以正确使用,所以您无需为此担心。