Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 3 of 3

Thread: Sending an array of structs to shader via an Uniform Buffer Object

  1. #1
    Newbie Newbie
    Join Date
    Sep 2016
    Posts
    2

    Sending an array of structs to shader via an Uniform Buffer Object

    Hi all!

    I've been banging my head with this for a few days now but I can't seem to get this working for some reason.

    I have an array of structs in C++ which I'd like to send to my fragment shader by using an uniform buffer object. As much I managed to gather during these past days, my code is currently in this format:

    C++:
    Code :
    struct Light {
        glm::vec4 Position;
        glm::vec4 Color;
        float Linear;
        float Quadratic;
        float Radius;
    };
     
    const GLuint NR_LIGHTS = 99;
    Light lights[NR_LIGHTS];
     
    shaderLightingPass.Use();
    GLuint uniformBlockIndexLights = glGetUniformBlockIndex(shaderLightingPass.Program, "LightBlock");
    glUniformBlockBinding(shaderLightingPass.Program, uniformBlockIndexLights, 0);
     
    // Uniform buffer object for lights
    GLuint uboLights;
    glGenBuffers(1, &uboLights);
    glBindBuffer(GL_UNIFORM_BUFFER, uboLights);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(lights), NULL, GL_DYNAMIC_DRAW);
    glBindBufferBase(GL_UNIFORM_BUFFER, uniformBlockIndexLights, uboLights);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    Then in Lighting Pass I set the individual lights in a for loop and set the values per array and after that send them to the UBO:
    Code :
     
    for (int i = 0; i < NR_LIGHTS; i++)
    {
        lights[i].Position = glm::vec4(lightPositions[i], 1.0f);
        lights[i].Color = glm::vec4(lightColors[i], 1.0f);
     
        const GLfloat constant = 1.0;
        const GLfloat linear = 0.7;
        const GLfloat quadratic = 1.8;
        lights[i].Linear = linear;
        lights[i].Quadratic = quadratic;
     
        const GLfloat maxBrigthness = std::fmaxf(std::fmaxf(lightColors[i].r, lightColors[i].g), lightColors[i].b);
        GLfloat radius = (-linear + sqrtf(linear * linear - 4 * quadratic * (constant - (256.0 / 5.0) * maxBrigthness))) / (2 * quadratic);
        lights[i].Radius = radius;
    }
     
    glBindBuffer(GL_UNIFORM_BUFFER, uboLights);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(lights), lights);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);


    In my fragment shader I've defined the struct and the uniform block like this:
    Code :
    const int NR_LIGHTS = 99; 
     
    struct Light {
        vec3 Position;
        vec3 Color;
        float Linear;
        float Quadratic;
        float Radius;
    };
     
    layout (std140) uniform LightBlock {
        Light lights[NR_LIGHTS];
    };

    And in the lighting calculations I'll sample the lights[i].values normally.

    I used an uniform array previously for this, but it requires me to set the uniform values individually in a for loop and as far as I've understood it's better to use an uniform buffer for this kind of purpose.

    What I'd like to know here is that what is the correct way to send this kind of an array of structs to the shader? Obviously this way isn't working right now. I've tried also a few other ways to send the data, e.g. sending each lights[i] array separately and including the offset in the glBufferSubData().

    Any help would be greatly appreciated. I've been looking around both here on the forums and docs, but I just can't seem to form the sending part correctly in my C++ code. Thank you in advance!

  2. #2
    Member Regular Contributor
    Join Date
    May 2016
    Posts
    466
    i'm not sure, but i think you have to make the memory of 1 struct "Light" consuming X * sizeof(vec4)

    right now, your struct Light (as it is defined in your c-code) consumes:

    struct Light {
    vec4 Position; // 1 x sizeof(vec4)
    vec4 Color; // 1 x sizeof(vec4)
    float Linear;
    float Quadratic;
    float Radius; // 3 x sizeof(float)...
    };

    by adding a "padding" variable at the end of the struct, it should work:

    struct Light {
    vec4 Position;
    vec4 Color;
    float Linear;
    float Quadratic;
    float Radius;
    float PADDING;
    };

    look for more explicit infos in here:
    http://www.opengl.org/registry/doc/glspec45.core.pdf
    (page 137/138)

    you can explicitly set the binding point index of the "LightBlock" by doing this:
    Code :
    layout (std140, binding = 1) uniform LightBlock {
        Light lights[NR_LIGHTS];
    };
    then in your c-code:
    Code :
    // allocate memory for light buffer
    glBindBuffer(GL_UNIFORM_BUFFER, mylightbuffer);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(Light) * NR_LIGHTS, NULL, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
     
    // bind light buffer to location 1
    glBindBufferBase(GL_UNIFORM_BUFFER, 1, mylightbuffer);
    now you should have access to "mylightbuffer" in your shaders

  3. #3
    Newbie Newbie
    Join Date
    Sep 2016
    Posts
    2
    Quote Originally Posted by john_connor View Post
    i'm not sure, but i think you have to make the memory of 1 struct "Light" consuming X * sizeof(vec4)

    right now, your struct Light (as it is defined in your c-code) consumes:

    struct Light {
    vec4 Position; // 1 x sizeof(vec4)
    vec4 Color; // 1 x sizeof(vec4)
    float Linear;
    float Quadratic;
    float Radius; // 3 x sizeof(float)...
    };

    by adding a "padding" variable at the end of the struct, it should work:

    struct Light {
    vec4 Position;
    vec4 Color;
    float Linear;
    float Quadratic;
    float Radius;
    float PADDING;
    };
    Hi and thanks for the quick reply! I tried this out and you are indeed right. It's working perfectly now for me.
    This is very embarrassing as I thought that I had understood how to do the layout correctly and there was nothing wrong with it. So I thought it had to be something else that's not working.
    I'll make sure to study the parts of the document you linked :)

    Thank you again!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •