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

时间:2018-05-22 21:22:49

标签: c++ opengl shader vbo voxel

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

uint8_t blocks[16][16][16]

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

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

2 个答案:

答案 0 :(得分:3)

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

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

如果您可以访问OpenGL 4.3+(对于大多数不超过6 - 8年的硬件来说,合理安全的赌注),那么Shader存储缓冲区将是最简洁的解决方案:

//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(或您更新缓冲区数据的首选方法),然后您就可以了

如果你只限于较旧的硬件,你确实有一些选择,但它们很快变得笨重:

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

答案 1 :(得分:1)

我按照@Xirema's answer中的方法做了第二种方法,但提出的建议略有不同。由于您的原始数据类型仅为uint8_t,因此直接使用SSBO或UBO将要求每个元素浪费3个字节或手动将4个元素打包到单个uint中。来自@Xirema的回答:

  

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

我完全同意。因此,我建议使用Texture Buffer Object (TBO), (a.k.a. "Buffer Texture")。 使用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以来可用的优势。