10

Right now I am trying to pass an array of ints into the fragment shader, and am doing this through a uniform array:

uniform int myArray[300];

And filling it outside the shader with glUniform1iv.

Unfortunately, uniform arrays larger than ~400 fail. I understand that I can use a "uniform buffer" instead, but can't seem to a find a full example of passing a large 1D array into a fragment shader with buffers or otherwise.

Could anyone supply such an example?

nbubis
  • 2,304
  • 5
  • 31
  • 46
  • @Vallentin - 5 lines of example code is not a tutorial. – nbubis Dec 18 '13 at 00:19
  • What version of GL are you targeting, and do you want this array to have variable length? You are on the right track with Uniform Buffer Objects (GL 3.1), they allow you to store much larger arrays but they cannot have variable length. If you want variable length, you can use a newer feature called Shader Storage Buffer Objects (GL 4.3). Now, since you mentioned OpenGL ES in your tags, I have to point out that the only truly portable solution is probably going to be a texture lookup. And even then, in GL ES 2.0 vertex texture lookups are an optional feature so this may limit you to fragment. – Andon M. Coleman Dec 18 '13 at 00:27
  • @AndonM.Coleman - I'm using 4.1. If they can be variable length, so much the better, but I can use a solution with fixed length as well. Thank you! – nbubis Dec 18 '13 at 00:36
  • Okay, is there any reason you included the OpenGL ES tag in your question? If you do not need to support ES, or have ES 3.0 as your minimum ES version then UBOs will work. – Andon M. Coleman Dec 18 '13 at 00:40
  • @AndonM.Coleman - sorry about the tag, that was not my intention. Could you show an UBO example with a large array? – nbubis Dec 18 '13 at 00:43
  • @Nathaniel That doesn't matter if it's not a tutorial or not, the purpose of Stack Overflow, isn't to simply ask and then get code. Your suppose to do all the learning by yourself, we're not suppose to provide you with example code or tell you how things work. THAT'S YOUR JOB! – vallentin Dec 18 '13 at 02:02

1 Answers1

11

This should get you started using a Uniform Buffer Object to store an array. Note that GL requires UBOs to have a minimum capacity of 16 KiB, the maximum capacity can be queried through GL_MAX_UNIFORM_BLOCK_SIZE.

Sample Fragment Shader (UBOs require OpenGL 3.1):

#version 140 // GL 3.1

// Arrays in a UBO must use a constant expression for their size.
const int MY_ARRAY_SIZE = 512;

// The name of the block is used for finding the index location only
layout (std140) uniform myArrayBlock {
  int myArray [MY_ARRAY_SIZE]; // This is the important name (in the shader).
};

void main (void) {
  gl_FragColor = vec4 ((float)myArray [0] * 0.1, vec3 (1.0));
}

OpenGL Code

const int MY_ARRAY_SIZE = 512;

GLuint myArrayUBO;
glGenBuffers (1, &myArrayUBO);

// Allocate storage for the UBO
glBindBuffer (GL_UNIFORM_BUFFER, myArrayUBO);
glBufferData (GL_UNIFORM_BUFFER, sizeof (GLint) * MY_ARRAY_SIZE,
              NULL, GL_DYNAMIC_DRAW);

[...]

// When you want to update the data in your UBO, you do it like you would any
//   other buffer object.
glBufferSubData (GL_UNIFORM_BUFFER, ...);

[...]

GLuint myArrayBlockIdx = glGetUniformBlockIndex (GLSLProgramID, "myArrayBlock");

glUniformBlockBinding (GLSLProgramID,     myArrayBlockIdx, 0);
glBindBufferBase      (GL_UNIFORM_BUFFER, 0,               myArrayUBO);

I am probably forgetting something, there is a reason I do not write tutorials. If you have any trouble implementing this, leave a comment.

UPDATE:

Note that the 0 used in glUniformBlockBinding (...) and glBindBufferBase (...) is a global identifier for the binding point. When used in conjunction with the std140 layout, this means that you can use this UBO in any GLSL program where you bind one of its uniform blocks to that binding location (0). This is actually extremely handy when you want to share something like your ModelView and Projection matrices between dozens of different GLSL programs.

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • It's usually good to add `int size` to the uniform block to store how many elements are actually used, since the array size is usually upper limit. (Just for the sake of the example :) – Jaa-c Dec 18 '13 at 01:52
  • Looks like OS X 10.9 doesn't like the shader code, probably because it's using an old version of GLSL. I'm (unfortunately) using openFrameworks, and I'm not quite sure how to force it to use a higher version. – nbubis Dec 18 '13 at 03:22
  • Texture was not mentioned in this answer at all. Is it because (everyone knows that) texture is significantly slower than UBO? – cppBeginner Dec 12 '18 at 11:27