如何将 int 数组发送到我的着色器

How to send an array of int to my shader

本文关键字:我的 int 数组      更新时间:2023-10-16

我正在制作一个体素引擎,我可以渲染一个块。我正在使用实例化渲染,这意味着我可以通过单个绘制调用渲染所有块。块的每个块都有一个 int(从 0 到 4095(来定义他的块类型(0 表示空气,1 表示污垢等(。我希望能够通过在我的片段着色器中应用良好的纹理来渲染我的块。我的块包含一个三维数组:

uint8_t blocks[16][16][16]

问题是我找不到将我的 int 数组发送到着色器的方法。我尝试使用VBO,但它没有意义(我没有得到任何结果(。我也尝试用glUniform1iv((发送我的数组,但我失败了。

  • 是否可以使用 glUniformX(( 将 int 数组发送到着色器?
  • 为了防止存储大数据,我可以用glUniformX((设置一个字节(uint8_t(而不是int吗?
  • 有没有一种好方法可以将这么多数据发送到我的着色器?
  • 实例化绘制是绘制具有不同纹理/块类型的相同模型的好方法。

出于所有目的和意图,这种类型的数据应被视为纹理数据。这并不意味着从字面上将其作为纹理数据上传,而是在考虑如何传输它时应该使用的思维框架。

或者,用更基本的术语来说:不要尝试将此数据作为统一数据传递。

如果您可以访问OpenGL 4.3+(对于大多数不超过6-8年的硬件来说,这是一个相当安全的选择(,那么Shader Storage Buffers将是最简洁的解决方案:

//GLSL:
layout(std430, binding = 0) buffer terrainData
{
int data[16][16][16];
};
void main() {
int terrainType = data[voxel.x][voxel.y][voxel.z];
//Do whatever
}
//HOST:
struct terrain_data {
int data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint ssbo;
GLuint binding = 0;//Should be equal to the binding specified in the shader code
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, GLsizeiptr size​, data.data​, GLenum usage);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

在此之后需要更新数据的任何时间点,只需绑定ssbo,调用glBufferData(或更新缓冲区数据的首选方法(,然后就可以开始了。

如果您仅限于较旧的硬件,则确实有一些选择,但它们很快就会变得笨重:

  • 您可以使用统一缓冲区,其行为与着色器存储缓冲区非常相似,但
    • 存储空间有限(大多数实现为 65kb(
    • 有其他可能与您的用例相关也可能无关的限制
  • 可以直接使用纹理,将地形数据转换为浮点值(如果硬件内部支持整数格式,则用作整数(,然后在着色器中转换回
    • 与几乎任何硬件兼容
    • 但着色器代码中需要额外的复杂性和计算

我支持@Xirema回答中列出的方法,但得出的建议略有不同。由于您的原始数据类型只是uint8_t,因此直接使用 SSBO 或 UBO 将需要为每个元素浪费 3 个字节或手动将 4 个元素打包到一个uint中。从@Xirema的回答:

出于所有目的和意图,此类数据应被视为纹理数据。这并不意味着从字面上将其作为纹理数据上传,而是在考虑如何传输它时应该使用的思维框架。

我完全同意这一点。因此,我建议使用纹理缓冲区对象(TBO((又名"缓冲区纹理"(。 使用glTexBuffer()基本上可以将缓冲区对象重新解释为纹理。在您的情况下,您可以将uint8_t[16][16][16]数组打包到缓冲区中,并将其解释为GL_R8UI"纹理"格式,如下所示:

//GLSL:
uniform usamplerBuffer terrainData;
void main() {
uint terrainType = texelFetch(terrainData, voxel.z * (16*16) + voxel.y * 16 + voxel.x).r
//Do whatever
}
//HOST:
struct terrain_data {
uint8_t data[16][16][16];
};
//....
terrain_data data = get_terrain_data();
GLuint tbo;
GLuint tex;
glGenBuffers(1, &tbo);
glBindBuffer(GL_TEXTURE_BUFFER, tbo);
glBufferData(GL_TEXTURE_BUFFER, sizeof(terrain_data)​, data.data​, usage);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_BUFFER, tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, tbo);

请注意,这不会将数据复制到某个纹理对象。访问纹理意味着直接访问缓冲区的内存。

TBO还有一个优势,即它们从OpenGL 3.1开始可用。