Search code examples
c++openglglslshaderfragment-shader

Defining multiple structs and setting them as uniforms in Fragment Shader GLSL


Im trying to create a material struct and a light struct that I will use in GLSL fragment shader. I can render the scene I have correctly when I have only one struct defined (either material or light) but when I define both of them together the glGetUniformLocation(...) calls return -1 for Material structs (order of defnitions does not matter) as if they are not there or not being used. I need to use the structs for the future use please do not ask me to use PODs. I want to learn how to upload multiple structs as uniforms. Thanks.

Here is the vertex shader:

#version 450

layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 norm;

layout (location = 2) out vec3 v_space_norm;
layout (location = 3) out vec3 v_space_pos;

layout(location = 0) uniform mat4 to_screen_space; // mvp
layout(location = 1) uniform mat4 to_view_space; //mv
layout(location = 2) uniform mat3 normals_to_view_space;
//layout(location = 4) float light_intensity;

void main() {
gl_Position = to_screen_space \* vec4(pos, 1.0);
v_space_norm = normals_to_view_space \* norm;
v_space_pos = (to_view_space \* vec4(pos, 1.0)).xyz;
}

and fragment shader:

#version 450
precision mediump float;
//------------ Structs ------------
// struct Light{
//      vec3 position;
//      float intensity;
// };

//------------ Variying ------------
layout (location = 2) in vec3 v_space_norm;
layout (location = 3) in vec3 v_space_pos;

//------------ Uniforms ------------
layout(location = 1) uniform mat4 to_view_space; //mv
//layout(location = 3) uniform vec3 light_position;

//layout(location = 4) uniform float light_intensity;
layout(location = 3) uniform struct{
     vec3 position;
     float intensity;
}light;

layout(location = 6) uniform struct{
     vec3 ka;
     vec3 kd;
     vec3 ks;
     float shininess;
} material;


out vec4 color;

void main() {
     vec3 v_space_norm = normalize(v_space_norm);
     vec3 l =  normalize( (to_view_space * vec4(light.position, 1)).xyz - v_space_pos);//normalize(l); //light vector
     vec3 h = normalize(l + vec3(0,0,1)); //half vector

     float cos_theta = dot(l, v_space_norm);
     if(cos_theta >= 0)
     {
          vec3 diffuse = material.kd * max(cos_theta,0);
          vec3 ambient = material.ka;
          vec3 specular= material.ks * pow(max(dot(h, v_space_norm),0), material.shininess);
          color = vec4(light.intensity * (specular + diffuse) + ambient, 1);
     }
     else
     {
          color = vec4(material.ka,1);
     }
}

And the way I set uniforms:

...
program->SetUniform("light.position", light.position);
program->SetUniform("light.intensity", light.intensity);
program->SetUniform("material.ka", material.ambient);
program->SetUniform("material.kd", material.diffuse);
program->SetUniform("material.ks", material.specular);
program->SetUniform("material.shininess", material.shininess);
...

The definition of SetUniform:

GLint location = glGetUniformLocation(glID, name);
if (location == -1)
{
    std::cout << "ERROR::SHADER::UNIFORM::" << name << "::NOT_FOUND"<<std::endl;
    return;
}
glUniform1f(location, value);//or what ever the type is there are definitions for all types!!!

I was unable to find anything online about my problem

I was hoping to see the teapot rendered and blinn shaded. And I can get that if I only define one struct and then upload the other properties as PODs.

So using the fragment shader below:

//------------ Uniforms ------------
layout(location = 1) uniform mat4 to_view_space; //mv
layout(location = 3) uniform vec3 light_position;

layout(location = 4) uniform float light_intensity;
// layout(location = 3) uniform struct{
//      vec3 position;
//      float intensity;
// }light;
...

//using light_position and light_intensity instead of light.position, light.intensity

Of course setUniform calls are also changed accordingly. enter image description here

But if I use both of the structs I get this: enter image description here

Edit: Instead of accessing via glGetUniformLocation(glID, name); I can manually set each variable via layout location number. Now the question becomes "Can I set the structs using the string variable names?"


Solution

  • Using named structs like:

    struct Light{
          vec3 position;
          float intensity;
    };
    

    And then using them as uniforms seems to be the correct way to go according to OpenGL wiki (I could have sworn I tried that but oh well...).

    layout(location = 3) uniform Light light;
    

    So,

    layout(location = 3) uniform struct{
         vec3 position;
         float intensity;
    }light;
    

    this is either not good or undefined behavior which is beyond my knowledge.