GLSL uniform only being updated by unrelated calls

11

I have an extremely basic GLSL program which is failing to properly update a uniform value after the first draw call. No errors are received from glGetError, no errors are reported in the info logs when compiling and linking the shaders, and all uniform locations are valid.

Vertex shader:

#version 120

uniform mat4 mvp;
uniform mat3 nmv;

attribute vec3 position;
attribute vec3 normal;

varying vec3 v_normal;

void main()
{
    v_normal = normalize(nmv * normal);
    gl_Position = mvp * vec4(position, 1.0);
}

Fragment shader:

#version 120

uniform vec3 lightDir;
uniform vec3 lightColor;
uniform vec3 materialColor;

varying vec3 v_normal;

void main()
{
    vec3 n = normalize(v_normal);
    float nDotL = max(0.0, dot(n, lightDir));

    gl_FragColor = vec4(materialColor * lightColor * nDotL, 1.0);
}

Rendering code:

glUseProgram(program);
glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvp);
glUniformMatrix3fv(nmvLoc, 1, GL_FALSE, nmv);
glUniform3fv(lightDirLoc, 1, lightDir);
glUniform3fv(lightColorLoc, 1, lightColor);

for (auto mesh: meshes)
{
    glUniform3fv(materialColorLoc, 1, mesh.color);
    mesh.draw();
}

The rendered meshes are all drawn in the color of the first mesh, indicating that after initially setting the materialColor uniform, the subsequent calls to change the uniform are ignored. However, here is a list of special conditions which independently allow the uniform to be updated properly:

  • Calling glUseProgram(program) within the loop.
  • Setting the mvp or the nmv uniforms within the loop.
  • Setting the lightDir uniform within the loop.
  • Removing one of the uniform vec3s from the shader program.

Please note that setting the lightColor uniform within the loop will not update the materialColor uniform. I have also checked GL_CURRENT_PROGRAM within the loop, and the shader remains bound throughout.

I have been trying to fix this for hours and absolutely cannot find the issue. This shader setup is so simple that I don't believe it's a driver bug. I'm using OpenGL 2.1 on Mac OS X 10.8.3 with a NVIDIA GeForce 9400M.

Here is a call trace for a single frame:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(1);
glUniformMatrix4fv(1, 1, 0, 0x7fff55512550); // mvp
glUniformMatrix3fv(5, 1, 0, 0x7fff55512528); // nmv
glUniform3fv(0, 1, 0x7fff55512670);          // lightDir
glUniform3fv(9, 1, 0x7fff555124e8);          // lightColor

// Mesh 0
glUniform3fv(8, 1, 0x7fab820cd7ec);          // materialColor
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 1);
glEnableVertexAttribArray(0);
glVertexAttribPointerARB(0, 3, GL_FLOAT, 0, 24, 0x00000000);
glEnableVertexAttribArray(2);
glVertexAttribPointerARB(2, 3, GL_FLOAT, 0, 24, 0x0000000c);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 21);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

// Mesh 1
glUniform3fv(8, 1, 0x7fab823000bc);          // materialColor
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 2);
glEnableVertexAttribArray(0);
glVertexAttribPointerARB(0, 3, GL_FLOAT, 0, 24, 0x00000000);
glEnableVertexAttribArray(2);
glVertexAttribPointerARB(2, 3, GL_FLOAT, 0, 24, 0x0000000c);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 24);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

// Mesh 2
glUniform3fv(8, 1, 0x7fab8231f8fc);          // materialColor
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 3);
glEnableVertexAttribArray(0);
glVertexAttribPointerARB(0, 3, GL_FLOAT, 0, 24, 0x00000000);
glEnableVertexAttribArray(2);
glVertexAttribPointerARB(2, 3, GL_FLOAT, 0, 24, 0x0000000c);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 21);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

// Mesh 3
glUniform3fv(8, 1, 0x7fab820cf41c);          // materialColor
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 4);
glEnableVertexAttribArray(0);
glVertexAttribPointerARB(0, 3, GL_FLOAT, 0, 24, 0x00000000);
glEnableVertexAttribArray(2);
glVertexAttribPointerARB(2, 3, GL_FLOAT, 0, 24, 0x0000000c);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 18);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

CGLFlushDrawable();

EDIT: Here is the code used to obtain the uniform locations. It is performed after the shaders have been compiled and linked, and all uniforms are verified to be valid.

GLint mvpLoc = glGetUniformLocation(program, "mvp");
GLint nmvLoc = glGetUniformLocation(program, "nmv");
GLint lightDirLoc = glGetUniformLocation(program, "lightDir");
GLint lightColorLoc = glGetUniformLocation(program, "lightColor");
GLint materialColorLoc = glGetUniformLocation(program, "materialColor");
opengl
glsl
asked on Stack Overflow May 17, 2013 by Chris Howard • edited May 18, 2013 by Chris Howard

3 Answers

0
  1. Sometimes driver will optimize some of your uniforms (even if it should not especialy with older drivers). to test that try to use your material color in vertex shader (copy it to some varying variable and in fragment shader use that varying variable instead of uniform, that sometimes works for me,... of course it lower performance a bit)

  2. try to use different profile version. In some cases newer drivers do not emulate older versions properly. Had you try core profile ? (i know its very tricky to implement with older code)

  3. you can try nvemulate to test different driver settings

sometimes forgotten glEnd(); makes a lot of trouble try to add this code before your frame rendering code:

glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
glEnd();
... here comes your rendering code

if this helps than you forgott to call glEnd(); somewhere or have glEnd; instead of glEnd();

[Edit1]

After your added the CPU side mesh draw related code its clear that you hard-coded all VBO and uniform positions but your fragment and vertex shaders have not static locations anywhere see

and look for glGetUniformLocation usage. Also in shaders you can state something like this:

layout(location = 0) in vec3 pos;

but that is for more recent GLSL in older version it might be a bit different and too lazy to search for syntax and keyword...

answered on Stack Overflow Aug 5, 2013 by Spektre • edited Apr 7, 2019 by Spektre
0

Still not solved?

what is the type of mesh.color

looking at the addresses passed into the call trace they seem rather high, perhaps you should be passing in an address to the data not the actual data, 0x7fab823000bc

glUniform3fv(materialColorLoc, 1, &mesh.color); perhaps try hard coding and using glUniform3f()

answered on Stack Overflow Aug 9, 2013 by Jet
0

From all the addresses in the trace I'd guess any vec3 you send to GL are of type float name[3], aren't they? Assuming this I cannot spot an error in your code. As you stated there's no error-state returned by glGetError, it MUST be a driver bug.

I saw in the trace that you start every mesh doing a glBindBuffer(GL_ARRAY_BUFFER, 0) - this is not really necessary. But anyhow, I cannot imagine this to be the source, either.

Sadly, you cannot test stuff like glBindUniformLocation before linking, as that is not part of OpenGL.

My experience shows: if you issue a glUniform3fv and all parameters are correct, then the uniform is updated and that change is visible right away. As that is not the case, here, it must be a driver bug, so the only things you could possibly do is: add other unnecessary calls to your rendering loop to make the change visible (which you already did if I read this correctly).

answered on Stack Overflow Sep 25, 2014 by St0fF

User contributions licensed under CC BY-SA 3.0