Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 2 of 2 FirstFirst 12
Results 11 to 18 of 18

Thread: VBO with dynamically changing number of points..!!

  1. #11
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    943
    Ok, I quickly setup a small test program and was pleased to see that my code worked almost correctly out of the box. I had one little error in my logic though. The first time the user clicks on the screen, there cannot be a valid index pair used to draw a line. This can only happen after there are at least two vertices. So I added checks to both updateBuffers() and render() (or in my case paintGL()) to make sure that updates and rendering only happen if we can actually draw at least a single line.

    I added a structure to better unify the buffer parameters and make it a little more flexible:

    Code :
    struct BufferParams
    {
        GLuint     buffers[2];
        GLsizeiptr vBufSize;
        GLsizeiptr iBufSize;
        GLsizeiptr vCompSize;
        GLsizeiptr iCompSize;
        GLenum     indexType;
    } bufferParams_;

    You'll also notice that some GL calls have gone due to optimization reasons. For instance, the buffers are now only bound when initBuffers() is called used for updates and rendering without unnecessary overhead. The program is written in Qt 4 but you shouldn't have any trouble understanding it. The functions initializeGL(), paintGL() and mousePressEvent() map to your begin(), render() and SDL event polling function respectively.

    So here's the full code:

    Code :
    #include "GLWidget.h"
    #include <QtGui/QMouseEvent>
     
    GLWidget::GLWidget() : numVertices_(-1)
    {
        resize(640, 480);
        maxVertices_ = 12;
     
        // calculate the size per vertex and per index pair
        bufferParams_.vCompSize = 4 * sizeof(GLfloat);
        bufferParams_.iCompSize = 2 * sizeof(GLubyte);
     
        // calculate buffer size
        bufferParams_.vBufSize  = maxVertices_ * bufferParams_.vCompSize;
        bufferParams_.iBufSize  = maxVertices_ * bufferParams_.iCompSize;
     
        // our current example will use indices in the range [0, 1, .., 255]
        bufferParams_.indexType = GL_UNSIGNED_BYTE;
    }
     
    GLWidget::~GLWidget()
    {
        glDeleteBuffers(2, bufferParams_.buffers);
    }
     
    void GLWidget::initializeGL()
    {
        // you can ignore this one
        glewInit();
     
        // init GL state
        glClearColor(1.f, 1.f, 1.f, 1.f);
        glLineWidth(2.f);
     
        // init matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, 640, 0, 480, 1.0, 10.0);
     
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0.f, 0.f, -4.f);
     
        // init vertex color attribute
        glColor3f(0.f, 0.f, 0.f);
     
        // init vertex and index buffer
        initBuffers();
    }
     
    void GLWidget::paintGL()
    {        
        glClear(GL_COLOR_BUFFER_BIT);
     
        // only issue a draw call if we got at least 2 vertices
        if(numVertices_ > 0)
        {            
            glDrawElements(GL_LINES, 2 * numVertices_, bufferParams_.indexType, NULL);
        }
    }
     
    void GLWidget::resizeGL(int width, int height)
    {
        glViewport(0, 0, width, height);    
    }
     
    void GLWidget::mousePressEvent(QMouseEvent* evt)
    {
        if(numVertices_ < 11)
        {
            ++numVertices_;
            float x = static_cast<float>(evt->x());
            float y = static_cast<float>(height() - evt->y());
            float coords[4] = {x, y, 0.f, 1.f};
            updateBuffers(coords);            
        }
    }
     
    void GLWidget::initBuffers()
    {
        glGenBuffers(2, bufferParams_.buffers);
        glBindBuffer(GL_ARRAY_BUFFER, bufferParams_.buffers[0]);
        glBufferData(GL_ARRAY_BUFFER, bufferParams_.vBufSize, NULL, GL_DYNAMIC_DRAW);
     
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferParams_.buffers[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferParams_.iBufSize,  NULL, GL_DYNAMIC_DRAW);
     
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
    }
     
    void GLWidget::updateBuffers(const float* coords)
    {    
        GLintptr vertexOffset = (numVertices_) * bufferParams_.vCompSize;
        glBufferSubData(GL_ARRAY_BUFFER, vertexOffset, bufferParams_.vCompSize, coords);    
     
        // we always want to add pairs of indices to the index buffer, so only do this
        // if we have at least 2 vertices in the vertex buffer
        if(numVertices_ > 0)
        {        
            GLintptr indexOffset = (numVertices_ - 1) * bufferParams_.iCompSize;  
            GLubyte  indices[]   = {numVertices_ - 1, numVertices_};            
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexOffset, sizeof(indices), indices);
        }
    }

    After clicking on the screen at least two time you'll see the first line and can add up to 10 more points. For example, I rendered the following curve where you can easily count the 12 vertices.
    Click image for larger version. 

Name:	dyn_updates.jpg 
Views:	25 
Size:	7.9 KB 
ID:	855
    HTH!

  2. #12
    Intern Contributor
    Join Date
    Jun 2009
    Posts
    51
    Oh that's very kind of you. Thank you very much. I'll try that and get back to you.

  3. #13
    Intern Contributor
    Join Date
    Jun 2009
    Posts
    51
    I'm still not getting it working. I'm getting full white background. Probably some problem with my code. I'll try with that. But it would have been nice if it was possible to make it more dynamic without keeping restriction on number of points. Now if I want to modify one of the point from these set of points, by picking a vertex and dragging a mouse pointer, I need to use only

    Code :
    glBufferSubData

    for the respective vertex's index isn't it?

  4. #14
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    943
    I'm still not getting it working.
    That's unfortunate, but I can't really do anything further after providing a working code sample. I think you might want to read some book or tutorial on OpenGL first before continuing. If you've already done so and think that you've gotten behind all the theory, do it again.

    But it would have been nice if it was possible to make it more dynamic without keeping restriction on number of points.
    In principle that's not a problem at all. The fact is, the GL doesn't provide any C++-like dynamic containers like std::vector so you're gonna have to deal with reallocation yourself. Of course, for that you'll need to either retrieve the current data stored in the buffer objects or store it in system memory as long as the poly-line exists. Then you'll resize the buffer objects to some size and transfer the data back using glBufferData(). A pseudo-algorithm could look like this:

    Code :
    on user input
    {
       if(max number of vertices reached)
       {
           if(max buffer size NOT reached)
           {
               resizeBuffers(newSize);
           }
           else
           {
                // this is just some error code to signal that no more allocation can take place 
                // nothing to do with GL!
                return OUT_OF_MEMORY_ERROR
           }
       }
     
       updateBuffers(coords);
    }

    Now, deleting vertices is a completely different thing. When clicking on the window, you'll need to determine if a vertex is hit and which vertex is hit. Then you need to determine the coordinates and indices to throw out of the buffers. You could calculate the new set of vertices and indices on the CPU and then simply update your buffers accordingly. In the course of removing vertices you might also attempt to shrink the buffers if the new number of vertices has gone low enough. I'm sure there are other possibilities but if you can't get the above method working I doubt thinking about more sophisticated methods wouldn't be too productive.

    There is already tons of information on algorithms for this so called picking problem out there and in 2D it's usual easy to deal with. Afterall, if you can uniquely identify each vertex using the coordinates of a mouse event you're already done. Just search for "2D picking" or the like.

    EDIT: BTW, I tried to code the example with as much forward compatible GL stuff as possible but since you're using the fixed-function pipeline anyway I wanted to give you an example using functions like glTranslatef(). Nowadays, using fixed-function stuff is not considered good practice and if you come up with something new, you should always opt for core GL, either 3.3 core or 4.3 core depending on the hardware you have at your disposal. If you have no GL3/4 capable hardware, you can still use shaders and throw most of the legacy stuff out so porting to GL3/4 later isn't that much of a problem.

    For a GL 3.3 core, Alfonse has come up with a very good tutorial here.
    Last edited by thokra; 08-24-2012 at 03:09 AM.

  5. #15
    Intern Contributor
    Join Date
    Jun 2009
    Posts
    51
    Hi,

    That's unfortunate, but I can't really do anything further after providing a working code sample. I think you might want to read some book or tutorial on OpenGL first before continuing. If you've already done so and think that you've gotten behind all the theory, do it again.
    Actually, the was working with already existing example and in that example I was trying out your code. I didn't start a new application all the way from scratch. So I'll try with a total new application. Logic seems to be perfect for me.

    There is already tons of information on algorithms for this so called picking problem out there and in 2D it's usual easy to deal with. Afterall, if you can uniquely identify each vertex using the coordinates of a mouse event you're already done. Just search for "2D picking" or the like.
    I have already implemented Picking and I get vertex index and coordinates which is being picked. Deleting and moving the picked vertex was my problem.

    Nowadays, using fixed-function stuff is not considered good practice and if you come up with something new, you should always opt for core GL, either 3.3 core or 4.3 core depending on the hardware you have at your disposal. If you have no GL3/4 capable hardware, you can still use shaders and throw most of the legacy stuff out so porting to GL3/4 later isn't that much of a problem.
    I have done these things with the glTranlatef, glVertex3f, glBegin(), glEnd(), things, which is I suppose, outdated and quite slow in performance. Therefore in order to improve the performance, I wanted to go for VBO. Rather I started with Shaders, but couldn't find it much easy as I failed to get any sample examples on shaders which depicts how to interactively draw a poly-line and move some points. So I gave up shaders and took-up VBOs, which is FFP based but comparably faster. I would be more happy to work with shading language. Suitable examples (related to shading language programming), papers related to development CAD based application would be appreciated.

    Thanks a lot.

  6. #16
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    943
    So I gave up shaders and took-up VBOs[..]
    See? This is why I recommended reading up on OpenGL.

    Shaders and buffer objects aren't mutually exclusive - quite the contrary. Using core OpenGL, you are required to use shaders! In the particular case of a regular vertex buffer, glDrawElements() will simply transfer the vertex attributes in the buffer to the shader. With the two most recent revisions you can even (or finally) explicitly write data directly to memory without really using the rasterizer at all. Stuff like ARB_shader_storage_buffer_object or ARB_shader_image_load_store are good examples (though the latter isn't technically interacting with buffer object but texture objects and texture buffer objects).

    A concurrent GL vertex shader tailored to your example is incredibly simple:

    Code :
    #version 430
     
    // this will be set when invoking glDrawElements() for each vertex necessary 
    // to render the primitives specified in the above GL call (e.g. 4 vertices for 2 line)
    // primitive assembly will take care of the rest
    in vec4 Position;
     
    // this is the projection matrix corresponding to what would be generated by glOrtho()
    uniform mat4 Projection;
     
    // this is what glTranslatef(0.f, 0.f, -1.f) effectively does
    // Note: In this example the translation is constant, however
    //          this could also be a uniform or any value you need,
    //          passed in from the application, sources from another
    //          buffer object or simply merged into translation matrix
    const vec4 Translation = vec4(0.0, 0.0, -4.0, 0.0);
     
    void main()
    {
        // the follow vec4 stores the final clip-space position
        gl_Position = Projection * (Position + Translation);
    }

    This is it and this isn't even the simplest approach as you can see from the comments. To get started with modern shader based OpenGL I strongly suggest working through Alfonse's tutorial from start to finish. Afterwards you'll be in a good place to get going.

  7. #17
    Intern Contributor
    Join Date
    Jun 2009
    Posts
    51
    In the particular case of a regular vertex buffer, glDrawElements() will simply transfer the vertex attributes in the buffer to the shader. With the two most recent revisions you can even (or finally) explicitly write data directly to memory without really using the rasterizer at all. Stuff like ARB_shader_storage_buffer_object or ARB_shader_image_load_store are good examples (though the latter isn't technically interacting with buffer object but texture objects and texture buffer objects).
    That makes some sense. Anyways, a sample shader code you wrote I suppose is a Vertex shader. Isn't it? Then what about fragment shader?

    To get started with modern shader based OpenGL I strongly suggest working through Alfonse's tutorial from start to finish.
    I had been through super-bible 5th edition. That doesn't mean I won't go through the above given source. But my first problem when I read that book was, how the input data will be passed to the shaders. I learnt, how to compile and use shaders in the application program, but how to supply input data was my doubt. May be I might have wrongly understood. So I definitely got to take a second round

  8. #18
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    943
    [..]but how to supply input data was my doubt.
    Remember in the above program the call to glVertexAttribPointer()? Remember the first parameter of that function? It's a unique index associated with a particular array of data - in this case with vertex positions defined using 4 components per vertex. Now, there are multiple ways to associate the input (in vec4 Position) in the shader with this unique index. Suppose we have defined and enabled an array like this:

    Code :
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);

    Now, you can see that the array associated with the index 0 has certain properties like 4 components of float for a single attribute, non-normalized, all packed (i.e. a stride of 0) and starting right at the beginning of the buffer object bound to GL_ARRAY_BUFFER (i.e. an offset of 0). If you called glDrawElements() while a shader program is active (i.e. made active using glUseProgram() ) no data would be transferred because the GL wouldn't have a clue how to associate inputs of the shader and the actual data. That's where the indices come into play. There are multiple methods of associating array indices with shader inputs:

    Code :
    // common setup program and stuff
    GLuint shaderProgram = glCreateProgram();
    //
    // set sources, compile, link, bla bla ...
    //
     
    // AFTER successful linkage you can do this - which is completely obsolete and inflexible like hell
    GLint location = glGetAttribLocation(shaderProgram, "Position");
     
    // A better approach is to bind and attribute index to a named input on the application level
    // As soon as you have a valid handle for a shader program this will work - if the arguments
    // are correct. No linkage, no querying of compiler-determined locations. Just define them 
    // yourself.
    glBindAttribLocation(shaderProgram, 0, "Position");
     
    // OR you can do this - explicitly bind the location INSIDE the shader
    // This way you don't even have to call glBindAttribLocation()
    #version 430
     
    layout(location = 0) in vec4 Position;
     
    // ...
     
    void main()
    {
        // ...
    }

    As you can see, the latter versions of binding have obvious advantages, giving you complete control of how to source buffers in your shaders. Afterwards, calls to glDrawElements() will lead to data being transfered to the vertex shader and voilą. A fragment shader which would achieve the same thing as using glColor3f(0.0, 0.0, 0.0) in the above example would simply look like this:

    Code :
    #version 430
     
    // explicit binding again - this time, however, we bin FragColor to one of the
    // currently active draw buffers. This is used for rendering to multiple render targets.
    layout(location = 0) out vec4 FragColor;
     
    void main()
    {
        // we want a black line - so use black as the color for every fragment
        FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }

    There you have it. This will draw the exact same thing but in a modern and completely core conforming way.

    Edit: Earlier I stated

    [..](as it turns out, NVIDIA and AMD drivers seem to simply do it anyway in some cases which is a violation of the spec IMHO).
    which is incorrect. The thing is, as soon as you link a program, indices which have not been explicitly specified will be automatically bound. So, if the automatically determined index happens to coincide with the index set with glVertexAttribPointer(), you'll have the same effect as binding it explicitly. However, this just proves why the approach of not doing explicit binding is not only inflexible but actually quite error prone. Intel, at least on Linux, doesn't seem to do implicit binding as the same code used to render on an NVIDIA GPU will not render anything with the Intel chip so they seem to violate the spec - again ...
    Last edited by thokra; 08-24-2012 at 06:11 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
  •