ユニフォーム バッファまたはシェーダ ストレージ バッファ オブジェクト内で「vec3」を使用する必要がありますか?

ユニフォーム バッファまたはシェーダ ストレージ バッファ オブジェクト内で「vec3」を使用する必要がありますか?の解決策は次のとおりです:

vec3 タイプはとてもいいタイプです。 3 つの float しか必要とせず、3 つの float しか必要としないデータがあります。そして、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 要素ベクトル型が存在しないふりをします .これには、3 行の列優先行列または 3 列の行優先行列が含まれます。唯一の型がスカラー、2 および 4 要素のベクトル (および行列) であると仮定します。そうすることで、あなたは非常に多くの悲しみを救うことができます.

vec3 + float の効果が必要な場合は、手動でパックする必要があります :

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

はい、data2and3.w を使用する必要があります 他の値を取得します。対処してください。

vec3 の配列が必要な場合 s、次にそれらを vec4 の配列にします 秒。 3 要素ベクトルを使用する行列についても同様です。 SSBO/UBO から 3 要素ベクトルの概念全体を追放するだけです。長い目で見れば、あなたの生活はずっと良くなるでしょう。

vec3 を避けるべき理由は 2 つあります。 :

C/C++ が行うことはできません

std140 を使用する場合 レイアウトの場合、おそらく GLSL の定義に一致する C または C++ でデータ構造を定義する必要があります。これにより、2 つの間のミックス &マッチが容易になります。そして 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++ コンパイラでは、sizeof 両方の Block_aBlock_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;
};

コンパイラが 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;
};

std140のルールにより 、各 vec3 開始する必要があります 16 バイト境界上。しかし vec3 消費しない 16 バイトのストレージ。消費するのは 12 だけです。そして float から vec3 の 4 バイト境界で開始できます 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 コンパイラがこれを正しく処理するため、それについて心配する必要はありません。