opengl - 我應該在統一緩衝區或著色器存儲緩衝區對像中使用`vec3`嗎?



angle opengl (1)

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;
};

然後,在我的C或C ++代碼中,我可以這樣做來創建匹配的數據結構:

struct UBO
{
  vector4 data1;
  vector3 data2;
  float data3;
};

struct SSBO
{
  vector4 data1;
  vector3 data2;
  float data3;
};

這是一個好主意嗎?


沒有! 永遠不要這樣做!

聲明UBO / SSBO時,假裝 不存在 所有3元素向量和矩陣類型。 假設唯一的類型是標量,2和4個元素向量(和矩陣)。 如果這樣做,您將為自己節省很多悲痛。

如果要使用vec3 +浮點效果,則應 手動 打包:

layout(std140) uniform UBO
{
  vec4 data1;
  vec4 data2and3;
};

是的,您必須使用 data2and3.w 來獲取其他值。 處理它。

如果要使用 vec3 的數組,則使它們成為 vec4 的數組。 使用3元素向量的矩陣也是如此。 只需消除SSBO / UBO中3元素向量的整個概念即可; 從長遠來看,您會過得更好。

您應該避免使用 vec3 原因有兩個:

它不會像C / C ++那樣

如果使用 std140 佈局,則可能要用C或C ++定義與GLSL中的定義匹配的數據結構。 這使兩者之間的混合和匹配變得容易。 而且,在大多數情況下, std140 佈局至少可以實現這一點。 但是當涉及到 vec3 時,其佈局規則與C和C ++編譯器的常規佈局規則不匹配。

考慮 vec3 類型的以下C ++定義:

struct vec3a { float a[3]; };
struct vec3f { float x, y, z; };

這兩個都是完全合法的類型。 這些類型的 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;
};

在大多數C ++編譯器上, Block_aBlock_f sizeof 均為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;
};

如果編譯器支持16字節對齊,則可以使用。 或者至少在 Block_aBlock_f 的情況下 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 ++對齊的規則不允許這樣的事情。 如果類型與X字節邊界對齊,則使用該類型將消耗X字節的倍數。

因此,匹配 std140 的佈局需要您根據使用的確切位置選擇一種類型。 如果後面跟隨 float ,則必須使用 vec3a ; 如果其後跟隨某種大於4字節對齊的類型,則必須使用 vec3a_16

或者,您不能只在著色器中使用 vec3 ,而避免所有這些增加的複雜性。

請注意,基於 alignas(8)vec2 不會出現此問題。 C / C ++結構和數組也不會使用適當的對齊說明符(儘管較小類型的數組有其自身的問題)。 在使用裸 vec3 才會 出現此問題。

實施支持模糊

即使您做對了所有事情,也知道實現會錯誤地實現 vec3 佈局規則。 一些實現將C ++對齊規則強加給GLSL。 因此,如果您使用 vec3 ,它將像C ++對待16字節對齊類型一樣對待它。 在這些實現中, vec3 後跟 float 將像 vec4 後跟 float

是的,這是實施者的錯。 但是由於您無法 修復 實現,因此您必須解決該問題。 而最合理的方法是完全避免使用 vec3

請注意,對於Vulkan(以及使用SPIR-V的OpenGL),SDK的GLSL編譯器可以實現此目的,因此您不必為此擔心。