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 10 of 22

Thread: Uniform Buffer Objects, dynamic sized arrays and lights

Hybrid View

  1. #1
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    24

    Uniform Buffer Objects, dynamic sized arrays and lights

    Hello, I'm new to this forum as a poster. I tried making the title as descriptive as possible so here goes:

    I'm new to UBOs and I've seen many different implementations and I don't think none of them serves my purpose and/or I can't understand UBOs completely. In my application, I have a number of lights that isn't fixed. I'd like to feed them to the GPU so I can use them in the shader code. Bonus: I'd prefer to have lights stored in the GPU much like the VBOs, where I just need to bind the buffer's ID (GLuint) to use it. The trick is that even if this is possible, you'd have to bind several UBOs, one for each light and bind a certain index to them so that it maps to the array on the shader code. Is there any way to do this?

    Right now, I'm looking at having my fragment shader like this:

    Code :
    struct Light {
       vec3 position;
       float padding;
    }
    layout (std140) uniform Lights {
       Light light[];
    }

    and then iterating through the lights. This dynamic allocation of arrays is possible in 4.3 as far as I know. However, I have problems in the C++ code:

    Initialization:
    Code :
    glGenBuffers(1,&m_lightsUBO); // this generates UBO, OK
    glBindBuffer(GL_UNIFORM_BUFFER, m_lightsUBO); // this binds it, OK
    glBufferData(GL_UNIFORM_BUFFER, /*size*/, /*data*/, GL_DYNAMIC_DRAW); // this allocates space for the UBO. 
    glBindBufferRange(GL_UNIFORM_BUFFER, /*buffer index*/, m_lightsUBO, 0, /*size*/); // this binds UBO to Buffer Index

    Though I don't need to specify the data at this point, I do need to specify a size... but I don't know how many lights I will need. This is a run-time value but if I move this code to a per-frame function I'll be creating a new UBO every frame and thus defeating the purpose. Also, I'm not sure what this Buffer Index should be.
    After the initialization, I can pretty much do this:

    Per Frame:
    Code :
    glBindBuffer(GL_UNIFORM_BUFFER,m_lightsUBO);
    glBufferSubData(GL_UNIFORM_BUFFER,0,data.size(),data.data());
    glBindBuffer(GL_UNIFORM_BUFFER,0);

    Which means I'm actually sending data to the GPU every frame, instead of just referencing a Buffer Object.

    I'd like to know if what I'm looking for is possible and if so, how to do it. If not, then I'd like to know the closest possible alternatives to what I want.

    Thank you in advance!

  2. #2
    Senior Member OpenGL Guru
    Join Date
    May 2009
    Posts
    4,948
    Uniform blocks must have an explicit size specific in the shader. Shader storage blocks however do not; they can be unbounded in size, with the size taken dynamically based on the range of the buffer object bound to the SSBO.

    SSBOs are only available in GL 4.3 hardware.

    Uniform blocks are probably sufficient for your needs. You can set a maximum hard limit on the number of lights, then simply pass fewer than that as a uniform variable:

    Code :
    #define MAX_NUM_TOTAL_LIGHTS 100
    struct Light {
      vec3 position;
      float padding;
    }
    layout (std140) uniform Lights {
      Light light[MAX_NUM_TOTAL_LIGHTS];
      int numLights;
    }

    So your uniform buffer would always have space for 100 lights, but you would also pass a variable that says how many to use.

    In any case, you will always have some kind of maximum size in your buffer object, no matter whether you use UBOs, SSBOs, buffer textures, or some other mechanism. You don't want to be constantly allocating buffers of arbitrary size. You allocate the maximum size, then fill it with whatever you need.

  3. #3
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    24
    I have 4.3 hardware, I'm even considering using beta 4.4 drivers.

    My example was simple, but I will actually require lights to have about 20N in size and I will also need an array with Materials which can also be 20N in size. The reason for having several materials is that I'll have the material index per pixel on a second shader pass that will do the light evaluations on a quad with a render-targeted texture. I will also be stress testing so I didn't really want to have a maximum size for the array. But even if I use your Uniform Block suggestion, do I keep a reference (GLuint) for the Buffer Objects so I can just bind them before drawing without passing the data to the shaders unless a light is modified? What I want is to have an array of structs in the shader which I can bound dynamically per frame. For example, binding the C++ struct lights[i].ubo to the shader variable lights[i] on a per-frame basis after having generated every lights[i].ubo on a initialisation stage by buffering the data on lights[i].position, lights[i].cutoff_angle and so on.

    EDIT: Oh I think I'm starting to understand what you can actually do with it. Could I, for example, allocate space for 100 lights, buffer the data into the shader, save the UBO id and then later when one of the lights gets updated I could buffer the data of that light into the shader without buffering all of the lights? I don't want to keep pushing things from the CPU to the GPU if the GPU could already have stored data like the lights... I would prefer to change for example light 33 and light 66 without sending the other lights over to the shader. So what I basically want is an array of light structs in the shader that may have specifically indexed lights being updated on demand.
    Last edited by FrankBoltzmann; 07-23-2013 at 09:05 PM.

  4. #4
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    24
    Should this be moved to OpenGL coding: Advanced or Beginners since it isn't just GLSL and there seem to be more people actively helping in that section?

  5. #5
    Junior Member Regular Contributor
    Join Date
    Nov 2012
    Location
    Bremen, Germany
    Posts
    167
    Maybe I'm reading you wrong, but: glBufferSubData updates a part of the buffer-object. So all you have to do is declare a uniform-block in the shader (with a sufficient-once-for-all number of lights) , create an equally-sized buffer in gl, bind it to the uniform block and - whenever you Change a light-setting - update the part that corresponds to it.
    I don't exactly know what you mean by 20N lights though. If the N is meant Alfonse's 100 then maybe a uniform buffer is too large as the overall-size of uniforms is limited. Then you'd have to use textures or Images to store and get-into-the-shader your light-Settings.

  6. #6
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    24
    N is the size of a float. In the OpenGL description of layout std140 it is said that data is stored in blocks of 4N if there is a vec3 or vec4, 2N if there is a vec2 and N if it's just floats.

    Each of my lights has 20N, thats 5 * vec4. So if there's 100 lights, that's 500 vec4s or 2000 floats. Same for materials so I could be passing 4000 floats per frame to the GPU and I really wanted to use the BUS as little as possible if I already have the data there.

    For materials, I really just need to collect all of my materials and send them once, unless a new material appears at run-time which is highly unlikely. Then when collecting all the objects, I pair them with a material ID so even if the objects have to be updated a lot (due to changes in one of the scenegraph matrices) the materials won't change so all I really need is the material ID to be passed to the shader on a per-primitive basis. For the lights, might be as you are saying: I allocate 2000 floats in the GPU's memory and I feel it the first time, then whenever a light is updated I send the part of it that needs updating. So if light 33 gets updated I do a glBufferSubData(GL_UNIFORM_BUFFER,33*20*sizeof(GLf loat),20*sizeof(GLfloat),data.data()); and that only sends 20 floats through the BUS to the GPU, while the other lights are already stored in the GPU's memory, right?
    Last edited by FrankBoltzmann; 07-25-2013 at 07:12 AM.

Posting Permissions

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