Nic*_*las 28 opengl opengl-es glsl vulkan
该vec3类型是一个非常好的类型.它只需要3个浮点数,我的数据只需要3个浮点数.我想在UBO和/或SSBO的结构中使用一个:
layout(std140) uniform UBO
{
vec4 data1;
vec3 data2;
float data3;
};
layout(std430) buffer SSBO
{
vec4 data1;
vec3 data2;
float data3;
};
Run Code Online (Sandbox Code Playgroud)
然后,在我的C或C++代码中,我可以这样做来创建匹配的数据结构:
struct UBO
{
vector4 data1;
vector3 data2;
float data3;
};
struct SSBO
{
vector4 data1;
vector3 data2;
float data3;
};
Run Code Online (Sandbox Code Playgroud)
这是一个好主意吗?
Nic*_*las 39
没有!永远不要这样做!
在声明UBO/SSBO时,假装不存在所有3元素向量和矩阵类型.假设唯一的类型是标量,2和4元素向量(和矩阵).如果你这样做,你将为自己节省很多悲伤.
如果你想要一个vec3 +一个浮点数的效果,那么你应该手动打包它:
layout(std140) uniform UBO
{
vec4 data1;
vec4 data2and3;
};
Run Code Online (Sandbox Code Playgroud)
是的,你必须data2and3.w用来获得另一个价值.处理它.
如果你想要vec3s的数组,那么使它们成为vec4s的数组.对于使用3元素向量的矩阵也是如此.从你的SSBO/UBO中消除3元素载体的整个概念; 从长远来看,你会好得多.
您应该避免以下两个原因vec3:
如果使用std140布局,那么您可能希望在C或C++中定义与GLSL中的定义匹配的数据结构.这使得两者之间的混合和匹配变得容易.和std140布局使得它至少可以在大多数情况下做到这一点.但是当它涉及到vec3s 时,它的布局规则与C和C++编译器的通常布局规则不匹配.
考虑以下vec3类型的C++定义:
struct vec3a { float a[3]; };
struct vec3f { float x, y, z; };
Run Code Online (Sandbox Code Playgroud)
这两种都是完全合法的类型.在sizeof这些类型的布局将匹配的大小和布局std140要求.但它与std140强加的对齐行为不匹配.
考虑一下:
//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;
};
Run Code Online (Sandbox Code Playgroud)
在大多数C++编译器,sizeof两者Block_a并Block_f会24.这意味着,offsetof b将是12.
但是,在std140布局中,vec3始终与4个字对齐.因此,Block.b将有16的偏移量.
现在,您可以尝试使用C++ 11的alignas功能(或C11的类似_Alignas功能)来解决这个问题:
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;
};
Run Code Online (Sandbox Code Playgroud)
如果编译器支持16字节对齐,这将起作用.或者至少,它将适用于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;
};
Run Code Online (Sandbox Code Playgroud)
根据规则std140,每个vec3必须从 16字节边界开始.但是vec3不消耗 16个字节的存储空间; 它只消耗12.并且因为float可以从4字节边界开始,a vec3后跟a float将占用16个字节.
但是C++对齐的规则不允许这样的事情.如果类型与X字节边界对齐,则使用该类型将消耗X个字节的倍数.
因此匹配std140的布局要求您根据其使用位置选择一种类型.如果它后面是a float,你必须使用vec3a; 如果它后跟一些超过4字节对齐的类型,你必须使用vec3a_16.
或者你可以不在vec3着色器中使用s并避免所有这些增加的复杂性.
请注意,alignas(8)基于 - vec2不会有这个问题.C/C++也不会使用正确的对齐说明符来构造和数组(尽管较小类型的数组也有自己的问题).只有在使用裸体时才会出现此问题vec3.
即使你做的一切都正确,已经知道实现错误地实现了vec3奇怪的布局规则.一些实现有效地将C++对齐规则强加给GLSL.因此,如果您使用a vec3,它会像C++一样对待16字节对齐类型.在这些实现中,a vec3后跟a float将像a vec4后跟a 一样工作float.
是的,这是实施者的错.但由于您无法修复实现,因此您必须解决它.而最合理的方法是vec3完全避免.
请注意,对于Vulkan,SDK的GLSL编译器可以正确使用,因此您无需为此担心.