1

I'm making a voxel engine and I can render a chunk. I'm using instanced rendering, meaning that I can render all of the chunk with a single draw call. Every blocks of a chunk has a single int (From 0 to 4095) that defines his block type (0 for air, 1 for dirt, etc...). I wanna be able to render my block by applying the good texture in my fragment shader. My chunk contains a tri-dimensionnal array :

uint8_t blocks[16][16][16]

The problem is that I can't find a way to send my array of int to the shader. I tried using a VBO but it makes no-sense (I didn't get any result). I also tried to send my array with glUniform1iv() but I failed.

  • Is it possible to send an array of int to a shader with glUniformX() ?
  • In order to prevent storing big data, can I set a byte (uint8_t) instead of int with glUniformX() ?
  • Is there a good way to send that much data to my shader ?
  • Is instanced drawing a good way to draw the same model with different textures/types of blocks.
4
  • Could you show some of the code that you used to attempt this transfer of data? It's very easy to transfer this kind of data between the host and the GPU, but it's also very easy to write the code responsible for this transfer wrongly. Commented May 22, 2018 at 21:25
  • @Xirema Well, I have erased that code.... For the moment I'm searching a good way to proceed, then I'll be sure that it's the good direction and I'll implement it Commented May 22, 2018 at 21:27
  • Please show the declaration of the function you are attempting to call, as well as the code you are calling it with and any errors (either compiler errors or runtime) that you may be receiving. Otherwise we're all simply guessing at what you're doing. Commented May 22, 2018 at 21:38
  • It's a very clear question for those who know that shaders do not generally seem to support int arrays. It is precisely the kind of problem I am looking for an answer to. Commented Oct 6, 2019 at 22:34

2 Answers 2

4

For all purposes and intents, data of this type should be treated like texture data. This doesn't mean literally uploading it as texture data, but rather that that's the frame of thinking you should be using when considering how to transfer it.

Or, in more basic terms: don't try to pass this data as uniform data.

If you have access to OpenGL 4.3+ (which is a reasonably safe bet for most hardware no older than 6-8 years), then Shader Storage Buffers are going to be the most laconic solution:

//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);

Any point after this where you need to update the data, simply bind ssbo, call glBufferData (or your preferred method for updating buffer data), and then you're good to go.

If you're limited to older hardware, you do have some options, but they quickly get clunky:

  • You can use Uniform Buffers, which behave very similarly to Shader Storage Buffers, but
    • Have limited storage space (65kb in most implementations)
    • Have other restrictions that may or may not be relevant to your use case
  • You can use textures directly, where you convert the terrain data to floating point values (or use as integers, if the hardware supports integer formats internally), and then convert back inside the shader
    • Compatible with almost any hardware
    • But requires extra complexity and calculations in your shader code
Sign up to request clarification or add additional context in comments.

Comments

1

I do second the approach as laid out in @Xirema's answer, but come to a slightly different recommendation. Since your original data type is just uint8_t, using an SSBO or UBO directly will require to either waste 3 bytes per element or to manually pack 4 elements into a single uint. From @Xirema's answer:

For all purposes and intents, data of this type should be treated like texture data. This doesn't mean literally uploading it as texture data, but rather that that's the frame of thinking you should be using when considering how to transfer it.

I totally agree to that. Hence I recommend the use of a Texture Buffer Object (TBO), (a.k.a. "Buffer Texture"). Using glTexBuffer() you can basically re-interpret a buffer object as a texture. In your case, you can just pack the uint8_t[16][16][16] array into a buffer and interpret it as GL_R8UI "texture" format, like this:

//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);

Note that this will not copy the data to some texture object. Accessing the texture means directly accessing the memory of the buffer.

TBOs also have the advantage that they are available since OpenGL 3.1.

4 Comments

That's a very good idea !! I'll try to implement it, thanks !
Well, I tried this and every chunks are 180 degrees flipped. It means that the data is stored a certain way and they are get in another order. Do you have any idea about that, do I need to flip something somewhere ?
Well, it is hard to tell without seeing the exact code. One thing is my 3D to 1D mapping terrainData, voxel.z * (16*16) + voxel.y * 16 + voxel.x. This assumes you're using standard C conventions, where you would access data[z][y][x] on the host. If you use data[x][y][z], then you also have to flip that calculation.
I used z,y,x like in your example, it was my bad, I thought it was for a x,y,z array. I corrected this, thanks !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.