openglshaderuniform

Setting uniforms in an unused shader affects output


I'm learning OpenGL and I've stumbled upon an issue where I'm not sure why it happens. I have a shader (vertex + fragment) for which I set uniform variables. The shader is responsible for both drawing a skybox as well as the objects (it differentiates via the uIsSkybox uniform variable). Here's the code for each:

#version 330 core 

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out vec2 oObjTexCoords;
out vec3 oSkyboxTexCoords; 

uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;

uniform bool uIsSkybox;

void main()
{
    if(uIsSkybox)
    {
        mat4 mvp = uProjection * mat4(mat3(uView)) * uModel;
        oSkyboxTexCoords = aPos;
        vec4 pos = mvp * vec4(aPos, 1.0);
        gl_Position = pos.xyww;
    }
    else 
    {
        mat4 mvp = uProjection * uView * uModel;
        oObjTexCoords = aTexCoords;
        gl_Position = mvp * vec4(aPos, 1.0);
    }
}

#version 330 core 

struct Material {
    sampler2D texture_diffuse;
    sampler2D texture_specular;
    float shininess;
};

in vec2 oObjTexCoords;
in vec3 oSkyboxTexCoords; 

out vec4 fragColor;

uniform Material material;
uniform sampler2D texture_diffuse1;
uniform samplerCube uSkyboxSampler;

uniform bool uIsSkybox;

void main() 
{
    if(uIsSkybox)
    {
        fragColor = texture(uSkyboxSampler, oSkyboxTexCoords);
    }
    else 
    {
//      fragColor = texture(texture_diffuse1, oObjTexCoords);
        fragColor = texture(material.texture_diffuse, oObjTexCoords);
    }

}

Here is the (desired) output:

Correct output

Before this I had a separate shader for the skybox. In code, I left the "setUniforms" code for this shader and only removed its usage (glUseProgram). I thought this would be enough since the shader is not being used anyway. However, it did affect my skybox and I'm not sure why.

model = glm::mat4(1.0f);
challengeShader.use();
challengeShader.setBool("uIsSkybox", true);
challengeShader.setMat4("uModel", model);
challengeShader.setMat4("uView", view);
challengeShader.setMat4("uProjection", projection);


//skyboxShader.use();
skyboxShader.setMat4("uModel", model);
skyboxShader.setMat4("uView", view);
skyboxShader.setMat4("uProjection", projection);

Incorrect output

Behind those setters are just calls to glGetUniformLocation and glUniform.... I've also made sure to confirm that both program IDs are not the same, and they are not. If I uncomment the setter calls of skyboxShader it works as desired. (It's really just the view matrix that causes this effect.) I also tried to give the uniforms separate names, but that makes no difference.

I'd like to know why this happens. I'll provide more information in case you need it.

PS: I had recently read that calls to texture() in the fragment shader should happen outside of branches. But in this case both argument types are different (samplerCube and vec3 vs sampler2D and vec2), so I'm not sure if that's possible in this case.

Edit: Here's the code for setMat4:

void Shader::setMat4(const std::string& name, glm::mat4 value) const {
    int location = glGetUniformLocation(ID, name.c_str());
    glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
}

Solution

  • The challenge shader has its uniforms:

    And so does the skybox shader - let's say they're in a different order:

    When you do skyboxShader.setMat4("uModel", model); it calls glGetUniformLocation(skyboxShader.ID, "uModel") which tells you that uModel is location 1 in skyboxShader. Then, it calls glUniformMatrix4fv(1, ...etc...) which sets location 1 in the current shader, which is challengeShader, which is uView. See the problem?

    glUniformWhatever always updates the uniforms in the currently used shader program. If you want to set uniforms in a different shader program, you have to use it first, or use glProgramUniformWhatever instead (OpenGL 4.1+)