Declaring array in GLSL based on uniform int - c++

In the program that I'm coding, I have to generate multiple lights sources and pass is to the shader. As this number is defined outside the shader, I though to pass it through an uniform int and use this to declare the arrays size.
uniform int numLights;
uniform vec4 ambientColor;
uniform vec4 specularColor;
uniform vec3 diffuseColor[numLights];
uniform vec3 vLightPosition[numLights];
However, I'm getting a lot of errors now:
Can't I do that? The thing is that I was thinking of the possibility of changing the number of predefined lights and to forget to update the shader, for example. My attempt was to prevent that to happen.

GLSL follows the C rule there, that array sizes must be known at compile time.
GLSL has a preprocessor but no means to supply preprocessor values other than as source code. So you'll need to generate the suitable #define statements as strings and supply them to glShaderSource prior to supplying the source for that shader — the calls to glShaderSource accumulate all text passed to them prior to compilation so that's the same as prefixing the extra strings to your source file.
You'll then need to recompile each time you change the number of lights, not just supply a uniform.

Related

Questions about uniform buffer objects

Is it guaranteed that if a uniform block is declared the same in multiple shader programs, say
uniform Matrices
{
mat4 ProjectionMatrix;
mat4 CameraMatrix;
mat4 ModelMatrix;
};
Will it have the same block index returned by glGetUniformBlockIndex(program, "Matrices")?
If the answer is yes, then I'm able to query the index of the block once and use it for all the shader programs that contain that block, right?
Second question: will ProjectionMatrix, CameraMatrix, ModelMatrix, always have the same layout order in memory, respectively? I'm asking this because the tutorial I read uses the next functions
// Query for the offsets of each block variable
const GLchar *names[] = { "InnerColor", "OuterColor",
"RadiusInner", "RadiusOuter" };
GLuint indices[4];
glGetUniformIndices(programHandle, 4, names, indices);
GLint offset[4];
glGetActiveUniformsiv(programHandle, 4, indices,
GL_UNIFORM_OFFSET, offset);
And I'm not sure if that's really needed, as long as I know the uniforms order inside the uniform block..?
will ProjectionMatrix, CameraMatrix, ModelMatrix, always have the same layout order in memory, respectively?
No. Here's what the standard states (emphasis mine):
If pname is UNIFORM_BLOCK_DATA_SIZE, then the implementation-
dependent minimum total buffer object size, in basic machine units, required to hold all active uniforms in the uniform block identified by uniformBlockIndex is returned. It is neither guaranteed nor expected that a given implementation will arrange uniform values as tightly packed in a buffer object. The exception to this is the std140 uniform block layout, which guarantees specific packing behavior and does not require the application to query for offsets and strides.
I'm not sure if that's really needed, as long as I know the uniforms order inside the uniform block..?
So, yes, the author is right in not assuming the layout is contiguous and does what's sensible (guaranteed to work always in all implementations): gets the uniform indices and assigns their values respectively.
Specifying layout(std140) will do the trick then, right?
Yes, you can avoid querying the location and uploading data every time by making use of both uniform buffer objects and std140. However, make sure you understand its alignment requirements. This information is detailed in ARB_uniform_buffer_object's specification. For an elaborate treatment with examples see OpenTK's article on Uniform Buffer Objects (UBO) using the std140 layout specification.
Is it guaranteed that if a uniform block is declared the same in multiple shader programs, will it have the same block index returned by glGetUniformBlockIndex(program, "Matrices")?
No. I've searched the OpenGL 3.3 specification which gives no such guarantees. From the standard's viewpoint, uniform blocks (default or named) are associated to a program, period. No existence/association of uniform blocks beyond a program is made in the specification.
Because there is no guarantee that uniform blocks will have the same index in different shader program, that means I need to call glBindBufferBase() everytime I switch programs, right?
Yes, see ARB_uniform_buffer_object's specification for an example.

GLSL: Are dynamically uniform expressions only calculated once?

If some calculations in a GLSL shader are only dependent on uniform variables, they could be calculated only once and used for every vertex/fragment. Is this really used in hardware? I got the idea after reading about "Uniform and Non-Uniform Control Flow" in the GLSL specification:
https://www.opengl.org/registry/doc/GLSLangSpec.4.40.pdf#page=30&zoom=auto,115.2,615.4
I would like to know if theres a difference between precalculating projection- and view-matrix for example.
It depends on the driver and the optimizations it is built to do, in direct3D there is an explicit api for that.
For example the simple point of
//...
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
main(){
gl_position = projection*view*model*pos;
}
most drivers will be able to optimize it to precalculate the MVP matrix and pass just that in a single uniform.
This is implementation defined and some drivers are better at inlining uniforms than other. One other optimization option is recompiling the entire program with inlined uniforms and optimize non-taken paths out.

GLSL, Array of textures of differing size

When doing multitexturing in GLSL, is there anyway to have an indexable array of samplers where each texture is a different size? This syntax isn't valid:
uniform sampler2D texArray[5];
Right now it seems like the only option is to individually create the samplers:
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D tex3;
uniform sampler2D tex4;
uniform sampler2D tex5;
But then I can't iterate through them, which is a real pain in the ass. Is there a solution?
This syntax isn't valid:
Says who? Arrays of samplers most certainly are valid (depending on the version). How you use them is a different matter.
GLSL 1.20 and below do not allow sampler arrays.
In GLSL 1.30 to 3.30, you can have sampler arrays, but with severe restrictions on the index. The index must be an integral constant expression. Thus, while you can declare a sampler array, you can't loop over it.
GLSL 4.00 and above allow the index to be a "dynamically uniform integral expression". That term basically means that all instantiations of the shader (within the same draw call) must get the same values.
So you can loop over a constant range in GLSL 4.00+, and index a sampler array with the loop counter. You can even get the index from a uniform variable. What you can't do is have the index depend on an input to the shader stage (unless that value is the same across all instances caused by the rendering command), or come from a value derived from a texture access (unless that value is the same across all instances caused by the rendering command), or something.
The only requirement on the textures placed in arrays of samplers is that they match the sampler type. So you have to use a GL_TEXTURE_2D on all the elements of a sampler2D array. Beyond that, the textures can have any number of differences, including size. The array exists to make coding easier; it doesn't change the semantics of what is there.
And remember: each individual element in the sampler array needs to be bound to its own texture image unit.
is there anyway to have an indexable array of samplers where each texture is a different size?
Not yet. Maybe this gets added to a later OpenGL version down the road, but I doubt it.
But then I can't iterate through them, which is a real pain in the ass. Is there a solution?
As a workaround you could use Array Textures and use only subregions of each layer. Use a vec4 array to store the boudaries of each picture on each layer.

(OpenGL 3.1 - 4.2) Dynamic Uniform Arrays?

Lets say I have 2 species such as humans and ponies. They have different skeletal systems so the uniform bone array will have to be different for each species. Do I have to implement two separate shader programs able to render each bone array properly or is there a way to dynamically declare uniform arrays and iterate through that dynamic array instead?
Keeping in mind performance (There's all of the shaders suck at decision branching going around).
Until OpenGL 4.3, arrays in GLSL had to be of a fixed, compile-time size. 4.3 allows the use of shader storage buffer objects, which allow for their ultimate length to be "unbounded". Basically, you can do this:
buffer BlockName
{
mat4 manyManyMatrices[];
};
OpenGL will figure out how many matrices are in this array at runtime based on how you use glBindBufferRange. So you can still use manyManyMatrices.length() to get the length, but it won't be a compile-time constant.
However, this feature is (at the time of this edit) very new and only implemented in beta. It also requires GL 4.x-class hardware (aka: Direct3D 11-class hardware). Lastly, since it uses shader storage blocks, accessing the data may be slower than one might hope for.
As such, I would suggest that you just use a uniform block with the largest number of matrices that you would use. If that becomes a memory issue (unlikely), then you can split your shaders based on array size or use shader storage blocks or whatever.
You can use n-by-1-Textures as a replacement for arrays. Texture size can be specified at run-time. I use this approach for passing an arbitrary number of lights to my shaders. I'm surprised how fast it runs despite the many loops and branches. For an example see the polygon.f shader file in the jogl3.glsl.nontransp in the jReality sources.
uniform sampler2D sys_globalLights;
uniform int sys_numGlobalDirLights;
uniform int sys_numGlobalPointLights;
uniform int sys_numGlobalSpotLights;
...
int lightTexSize = sys_numGlobalDirLights*3+sys_numGlobalPointLights*3+sys_numGlobalSpotLights*5;
for(int i = 0; i < numDir; i++){
vec4 dir = texture(sys_globalLights, vec2((3*i+1+0.5)/lightTexSize, 0));
...

How does glGetUniformBlockIndex know whether to look in the vertex shader or the fragment shader?

I start by attaching to my program both shaders and then call glGetUniformBlockIndex to bind the uniform buffers. I would expect a glBindShader function to let me specify which shader I want to parse to find the uniform, but there is no such thing.
How can I tell OpenGL which shader it should look into?
Assuming the program has been fully linked, it doesn't matter. Here are the possibilities for glGetUniformBlockIndex and their consequences:
The uniform block name given is not in any of the shaders. Then you get back GL_INVALID_INDEX.
The uniform block name given is used in one of the shaders. Then you get back the block index for it, to be used with glUniformBlockBinding.
The uniform block name given is used in multiple shaders. Then you get back the block index that means all of them, to be used with glUniformBlockBinding.
The last part is key. If you specify a uniform block in two shaders, with the same name, then GLSL requires that the definitions of those uniform blocks be the same. If they aren't, then you get an error in glLinkProgram. Since they are the same (otherwise you wouldn't have gotten here), GLSL considers them to be the same uniform block.
Even if they're in two shader stages, they're still the same uniform block. So you can share uniform blocks between stages, all with one block binding. As long as it truly is the same uniform block.