matus-chochlik / oglplus

OGLplus is a collection of open-source, cross-platform libraries which implement an object-oriented facade over the OpenGL® (version 3 and higher) and also OpenAL® (version 1.1) and EGL (version 1.4) C-language APIs. It provides wrappers which automate resource and object management and make the use of these libraries in C++ safer and more convenient.
http://oglplus.org/
Boost Software License 1.0
495 stars 71 forks source link

Uniform Buffer Objects in oglplus? #112

Open StepUpSenpai opened 9 years ago

StepUpSenpai commented 9 years ago

Hi again, I have a valid, minimal OpenGL code that produces desired results:

        float test[] = { 0.5f, 0.0f, 0.0f, 0.0f };
    GLint programID; 
    glGetIntegerv(GL_CURRENT_PROGRAM, &programID);

    GLuint blockIndex = glGetUniformBlockIndex( programID  , "MatrixBlock");
    glUniformBlockBinding(programID, blockIndex, 1);

    GLuint UBOindex;
    glGenBuffers(1, &UBOindex);
    glBindBuffer(GL_UNIFORM_BUFFER, UBOindex);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(test), test, GL_DYNAMIC_DRAW);
    glBindBufferBase(GL_UNIFORM_BUFFER, 1, UBOindex);

In the vertex shader code I simply translate the vertex position by that test vector that I've got in "MatrixBlock" uniform block.

The problems start when I try to express the same thing using oglplus:

        float test[] = {0.5f,0.0f,0.0f,0.0f};
        oglplus::UniformBlock uniblock(program, "MatrixBlock"); 
    uniblock.Binding(1);

       oglplus::Buffer UBO;
    UBO.Bind(oglplus::Buffer::Target::Uniform);
        oglplus::Buffer::Data(oglplus::Buffer::Target::Uniform, sizeof(test), test);
    UBO.BindBaseUniform(1);

When I switch to this oglplus-based code my geometry simply doesn't get translated at all in vertex shader (as if there are only zeroes in the test vector).

matus-chochlik commented 9 years ago

You don't specify the BufferUsage parameter in the OGLplus code (but I'm not sure yet if this is the reason it does not work)

but try the following:

oglplus::Buffer::Data(oglplus::Buffer::Target::Uniform, sizeof(test), test, oglplus::BufferUsage::DynamicDraw);

StepUpSenpai commented 9 years ago

Adding the usage didn't work.

Also, sorry if that's not the right place to ask this, but I couldn't find anything in the documentation. One of the features of oglplus is supposed to be the interoperability with "raw" opengl instructions but I can't find any way to retrieve the "raw" program id that I would be able to pass to functions like above glUniformBlockBinding, By this I mean something like: glUniformBlockBinding(program.getID(), blockIndex, 1); where "program" is of type oglplus::Program.

Of course I can do program.use() and then retrieve the id with a regular opengl call like above glGetIntegerv(GL_CURRENT_PROGRAM, &programID); but it doesn't seem like a elegant and/or proper way to do this

It looks like this issue is related to binding operations so if there was a way to retrieve programID from oglplus::Program and bufferID from oglplus::Buffer than I could resolve this issue by using the Program and Buffer classes like normal, then getting IDs from them and passing them to raw binding instructions (glBindBufferBase and glUniformBlockBinding).

matus-chochlik commented 9 years ago

Hmm, OK I'll try to create a working code using UBOs and have a look at this.

In order to get the GL name of a wrapped object (program, shader, texture,...) You can use the GetGLName(program) or GetName(program) function.

matus-chochlik commented 9 years ago

I have a question: do you issue the GL draw calls in the same function where the code above is executed or somewhere else?

StepUpSenpai commented 9 years ago

I was issuing the draw calls in separate function, of course after binding all the buffers first. Currently my code looks like this:

void Camera::uploadMatrices()
{

    if (!Camera::initialized)
        throw new std::exception("Can't upload matrices when camera is not initialized.");

    oglplus::ModelMatrixf MVP = Camera::projection * Camera::zoomOut *  Camera::view *  Camera::baseTranslation;

    UBO->Bind(oglplus::Buffer::Target::Uniform);
    oglplus::Buffer::Data(oglplus::Buffer::Target::Uniform, sizeof(GLfloat)*MVP.Size(), MVP.Data(),oglplus::BufferUsage::DynamicDraw);  
    glBindBufferBase(GL_UNIFORM_BUFFER, 1, oglplus::GetGLName(*UBO));
}

and in my program class:

void GLSLProgram::bindUBO()
{
    GLint programID = oglplus::GetGLName(this->program);
    GLuint blockIndex = glGetUniformBlockIndex(programID, "MatrixBlock");
    glUniformBlockBinding(programID, blockIndex, 1);
}

Drawing with:

void Domain::draw()
{
    glLineWidth(2.0f);

    this->program.useThisProgram();
    this->VAO.Bind();
    this->VBO.Bind(oglplus::Buffer::Target::Array);
    this->IBO.Bind(oglplus::Buffer::Target::ElementArray);

    oglplus::Context().DrawElements(oglplus::PrimitiveType::Lines, 24, oglplus::DataType::UnsignedShort);

}

This way it works fine. Sorry, but I couldn't spend more time experimenting with binding in oglplus itself and ended up using raw opengl calls. I am also really sorry for replying so late.

matus-chochlik commented 9 years ago

Hi, sorry for the late response, I've been rather busy lately.

The problem was probably caused by the fact that You created a buffer object in one function, set it up and uploaded the data,

oglplus::Buffer UBO;
UBO.Bind(oglplus::Buffer::Target::Uniform);
oglplus::Buffer::Data(oglplus::Buffer::Target::Uniform, sizeof(test), test);
UBO.BindBaseUniform(1);

but when the process went out of scope of that function, the UBO buffer got destroyed and the underlying GL object deleted. There are some examples using UBOs and I've tested them on several machines and everywhere they work fine.

jose-villegas commented 9 years ago

Are there more examples of UniformBuffers except for examples/standalone/025_bitmap_font_text.cpp ?

I'm doing exactly the same but trying to update a more complex uniform block with a array of Light structs.

struct Attenuation
{
    float constant;
    float linear;
    float quadratic;
};

struct Light {
    float angleInnerCone;
    float angleOuterCone;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    vec3 position;
    vec3 direction;

    Attenuation attenuation;

    uint lightType;
};
...
layout (std140) uniform AvailableLights {
    Light light[N_LIGHTS];
};

I have in the C++ side a identical class to that struct, and a vector of that class. The rest is exactly the same, I bind the uniform buffer to the program, and use a uniform buffer object to pass the vector of Light using the same binding point but it is not working. I may end up using raw C OpenGL to do this or just standard uniforms.

matus-chochlik commented 9 years ago

Hi, there are several other examples using UBOs:

oglplus/021_overdraw.cpp oglplus/029_gpu_sort_tfb.cpp oglplus/032_bar_grid.cpp oglplus/031_motion_blur.cpp oglplus/014_multi_cube_ub.cpp oglplus/031_neon.cpp oglplus/031_fog.cpp oglplus/032_object_tracking.cpp

HTH