OpenGLShaderProgram::Uniform::set confusion


#1

Hi,

while learning some OpenGL, I stumbled upon one thing I don’t understand.

OpenGLShaderProgram::Uniform::set has multiple overloaded implementations. I now want to set a uniform vec4 uMyVec in my fragment shader. std::unique_ptr<OpenGLShaderProgram::Uniform> myVec represents this uniform. For debugging purposes I assign it’s value inside the shader to the colour of the shape I’m drawing.

The following works, e.g. the shape becomes completely white:
myVec->set (1.0f, 1.0f, 1.0f, 1.0f);

I’d expect this call to do the same:

float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
myVec->set (white, 4);

However the second version results in an invisible shape. Did I get something about the pointer + numValues overload completely wrong or is that a bug?

By the way, to be a bit more specific about what I’m actually trying to do: I’m using glm as it has a few more sophisticated linear algebra operations compared to the onboard JUCE classes. So my final approach is to call myVec->set (glm::value_ptr (someGlmVector4), 4); but this doesn’t work - however glm::value_ptr returns some valid results, so this can not be the problem.


#2

Okay, I finally got it.

A look at the OpenGL docs show that there are a lot of different possible calls to set uniforms: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUniform.xhtml
(By the way, being used to the nice looking JUCE online documentation I am shocked of how ugly the khronos documentation website looks :grimacing:)

To set a uniform vec4 there are those two possibilities:

void glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2,GLfloat v3);

void glUniform4fv (GLint location, GLsizei count, const GLfloat *value);

The first option is more or less obvious (side note to the JUCE team - why is the fourth parameter a normal float instead of a GLfloat in the JUCE-Implementation? --> https://github.com/WeAreROLI/JUCE/blob/06c3674e510f991d88f881203aedd85a60fbced9/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.cpp#L156)

However the second option has the count argument. According to the khronos docs, this allows the user to assign an array of vec4 uniforms. Coming back to my initial post, this proved to work:

float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
glUniform4fv (myVec->uniformID, 1, white); 

As well as what I really want to do with glm:

glUniform4fv (uniforms.lightPosition->uniformID, 1, glm::value_ptr (lightPosition));

Looking further at the JUCE codebase, I found out that OpenGLShaderProgram::Uniform::set (const GLfloat *values, int numValues) calls glUniform1fv (uniformID, numValues, values) which might be used to assign an array of uniform float scalars, but not to assign a uniform vector.

So basically this was my fault as a beginner as I didn’t read the documentation of the underlying OpenGL API. This explains why assigning a vec4 this way didn’t work.

To avoid this pitfall for other OpenGL newbies my proposal to the JUCE team would be at least to change the documentation a bit so that it clearly says Sets an array of scalar float uniforms instead of the current Sets a vector float uniform as vector is some kind of “reserved” OpenGL keyword that was misleading in this case for me.

Furthermore, what about adding glUniform2fv, glUniform3fv and glUniform4fv setter functions to OpenGLShaderProgram::Uniform and maybe while in that section of code even overload the OpenGLShaderProgram::Uniform::setMatrix4 to directly accept a Matrix3D<float> which would make this API a bit more complete and a lot nicer to use? @fabian, @jules (I think you are the ones mainly in charge of all this UI stuff?)


#3

Man, this quote should be a sticky post for all the newbs flooding the forum lately!!!


#4

:rofl:

Glad to make you happy with this little bit of self-criticism (which in my opinion is crucial for getting better in anything one learns…)


#5

Print that out and tape it to the bottom of your monitor!