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 1 of 2 12 LastLast
Results 1 to 10 of 18

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

  1. #1
    Intern Newbie
    Join Date
    Jun 2009
    Posts
    49

    VBO with dynamically changing number of points..!!

    Hi,

    This is in regards to VBOs. I am trying to implement a polyline editor, where polyline is being drawn using mouse clicks. It is up too the user, how long polyline will be drawn. Thus, the number of vertices are not know in advance. Most of the examples show, the static number of vertices. How can I use VBO for dynamically changing number of vertices?

    Thanks

  2. #2
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    893
    Either you predetermine the maximum number of vertices beforehand, i.e. the user is able to draw a maximum of n points, or you allocate new storage as needed using glBufferData(). Updates to buffer objects can either be done using glBufferData(), glBufferSubData() or by mapping the buffer objects address space to client memory using glMapBuffer().

    For instance, if you have a buffer which permits storing 12 vertices, you can do the following:

    WARNING: Untested code!

    Code :
    // the size computation assumes 12 vertices with 4 components each
    GLsizeptr vertexBufferSize = 12 * 4 * sizeof(GLfloat);
     
    // we'll need two indices per line segment, so the total index buffer size is 2 * maxVertices = 24.
    // indices aren't surpassing 256 elements in this case so choosing indices of type GL_UNSIGNED_BYTE is sufficient.
    GLsizeptr indexBufferSize = 24 * sizeof(GLubyte);
     
    // store buffer handles somewhere - we'll use buffers[0] as VBO handle, and buffers[1] as index buffer handle.
    GLuint buffers[2];
     
    ...
     
    void initBuffers()
    {
      glGenBuffers(2, &buffers);
      glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
      glBufferData(GL_ARRAY_BUFFER, vertexBufferSize, NULL, GL_DYNAMIC_DRAW);
     
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSize,  NULL, GL_DYNAMIC_DRAW);
     
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
    }

    As you can see, there aren't any vertex attribs stored yet. Now, if a user clicks on the screen and thus adds a vertex, you could do this.

    Code :
    // store current number of verts somewhere it can be accessed easily
    int numVertices = 0;
     
    ...
     
    // user adds a vertex - if numVertices < 11 
    // new coordinates are stored in some memory location
    ++numVertices;
     
    // this function takes pointer to 4 coordinates stored contiguously in memory, i.e. a 4 component C-Array
    // alternatively, a reference to a const std::vector with 4 components will do fine as well
    void updateBuffers(float* coords)
    {
      GLintptr vertexOffset = 4 * (numVertices - 1) * sizeof(GLfloat);
      GLintptr indexOffset = 2 * (numVertices - 1) * sizeof(GLubyte);  
      GLubyte indices[] = {numVertices - 1, numVertices};
     
      // append new vertex 
      glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
      glBufferSubData(GL_ARRAY_BUFFER, vertexOffset, 4 * sizeof(GLfloat), coords);
     
      // append new indices
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
      glBufferSubData(GL_ELEMENT_ARRAY_BUFFFER, indexOffset, sizeof(indices), indices);  
    }

    Now, when rendering with the above buffer, you'll do something like this:

    Code :
     
    void renderPolyLine()
    {
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
      glDrawElements(GL_LINES, numVertices * 2, GL_UNSIGNED_BYTES, NULL);
    }

    Again, this code is untested and not guaranteed to work but it should give you an idea. Also, note that this code isn't using vertex array objects which are mandatory when using GL 3.2+ core. However, since you didn't state which version you are programming towards, I left it compatible. There are other performance improvements which are obvious but until you get the basic stuff working you shouldn't worry about optimization.

    Let us know if this works for you.

  3. #3
    Intern Newbie
    Join Date
    Jun 2009
    Posts
    49
    Hi Thokra,

    Thank you very much for your reply. I tried the code you sent. The code compiles after modifying two statements. But nothing gets displayed, before and after mouse clicks. I wonder the NULL which is passed in glDrawElements might be causing problem. I am using OpenGL 2.1 version on Windows XP.

    And let me confirm myself, the code which you supplied above, is an example showing how to dynamically draw polyline containing 'n' number of vertices (12 in your example). Isn't it?

    Thanks

    Regards
    Rakesh Patil

  4. #4
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    893
    The code compiles after modifying two statements.
    What went wrong?

    And let me confirm myself, the code which you supplied above, is an example showing how to dynamically draw polyline containing 'n' number of vertices (12 in your example). Isn't it?
    Yes, at least it's supposed to be.

    But nothing gets displayed, before and after mouse clicks. I wonder the NULL which is passed in glDrawElements might be causing problem.
    If a buffer object is bound to GL_ELEMENT_ARRAY_BUFFER, the 4th argument to glDrawElements() is merely an offset into the buffer's datastore.

    Do you get any GL errors? Is everything setup correctly so you can issue GL commands, for instance if you call glClearColor(1.f, 0.f, 0.f, 1.f) and glClear(GL_COLOR_BUFFER_BIT), do you see a red window background? Are the model-view-matrix, the projection and the viewport set correctly?

  5. #5
    Intern Newbie
    Join Date
    Jun 2009
    Posts
    49
    What went wrong?
    It's a typo error. An extra 'F' has come here

    Code :
    [LEFT][COLOR=#3E3E3E]glBufferSubData(GL_ELEMENT_ARRAY_BUFFFER, indexOffset, sizeof(indices), indices);[/COLOR][/LEFT]

    and similarly extra 's' has come in another sentence.

    Yes, at least it's supposed to be.
    That's what I also feel. Overall logic seems to be correct

    If a buffer object is bound to GL_ELEMENT_ARRAY_BUFFER, the 4th argument to glDrawElements() is merely an offset into the buffer's datastore.
    Well, actually there is no binding being done to any data structure. Can this be a reason?

    Do you get any GL errors? Is everything setup correctly so you can issue GL commands, for instance if you call glClearColor(1.f, 0.f, 0.f, 1.f) and glClear(GL_COLOR_BUFFER_BIT), do you see a red window background? Are the model-view-matrix, the projection and the viewport set correctly?
    I get the previously rendered image once I rub the application. Changing background color using glClearColor() has no impact and again the previous successfully rendered image is displayed. Well, here's the entire code. I have created a separate files.

    Below code is ctest.h
    Code :
    #ifndef __ctest_h_
    #define __ctest_h_
     
    struct ctest
    {
      int numVertices;
      void Begin();
      void End();
      void Render();
      void InitBuffers();
      void UpdateBuffers(float *coord);
    };
     
    #endif

    The corresponding cpp file goes like this
    Code :
    #include "ctest.h"
     
    // the size computation assumes 12 vertices with 4 components each
    GLsizeptr vertexBufferSize = 12 * 4 * sizeof(GLfloat);
     
     
    // we'll need two indices per line segment, so the total index buffer size is 2 * maxVertices = 24.
    // indices aren't surpassing 256 elements in this case so choosing indices of type GL_UNSIGNED_BYTE is sufficient.
    GLsizeptr indexBufferSize = 24 * sizeof(GLubyte);
     
     
    // store buffer handles somewhere - we'll use buffers[0] as VBO handle, and buffers[1] as index buffer handle.
    GLuint buffers[2];
     
    void ctest::InitBuffers()
    {
      glGenBuffers(2, &buffers);
      glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
      glBufferData(GL_ARRAY_BUFFER, vertexBufferSize, NULL, GL_DYNAMIC_DRAW);
     
     
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSize,  NULL, GL_DYNAMIC_DRAW);
     
     
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);
    }
     
    void ctest::Begin()
    {
     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
     glShadeModel(GL_SMOOTH);
     
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     
     gluPerspective(45.0f, 640.0f / 480.0f, 0.1f, 100.0f);
     
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     glTranslatef(0.0f, 0.0f, -4.0f);
     
     numVertices = 0;
     
     InitBuffers();
    }
     
    void ctest::End()
    {
     glDeleteBuffers(2, buffers);
    }
     
    void ctest::Render()
    {
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);  glDrawElements(GL_LINES, numVertices * 2, GL_UNSIGNED_BYTES, NULL);
    }
     
    void ctest::UpdateBuffers(float* coords)
    {
      GLintptr vertexOffset = 4 * (numVertices - 1) * sizeof(GLfloat);
      GLintptr indexOffset = 2 * (numVertices - 1) * sizeof(GLubyte);  
      GLubyte indices[] = {numVertices - 1, numVertices};
     
     
      // append new vertex 
      glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
      glBufferSubData(GL_ARRAY_BUFFER, vertexOffset, 4 * sizeof(GLfloat), coords);
     
     
      // append new indices
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
      glBufferSubData(GL_ELEMENT_ARRAY_BUFFFER, indexOffset, sizeof(indices), indices);  
    }

    In main function, this is how it is
    Code :
    ctest *test = new ctest;
     
    test->Begin();
     
    bool Exit = false;
    while(!Exit)
    {
    	SDL_Event event;
    	while(SDL_PollEvent(&event))
    	{
    		switch (event.type)
    		{
    			case SDL_MOUSEBUTTONDOWN:
    			{
    				float coords[4];
    				coords[0] = static_cast<float>(button.x);
    				coords[1] = static_cast<float>(480 - button.y);
    				coords[2] = coords[3] = 0;
    				test->UpdateBuffers(coords);
    			}
    			break;
    			case SDL_QUIT:
    			case SDL_KEYUP:
    				Exit = true;
    				break;	
    		}
    	}
    	test->Render();
     
    	SDL_GL_SwapBuffers();
     
    	int Error;
    	if((Error = glGerError()) != GL_NO_ERROR)
    	{
    		const char *message = 
    		    (const char *)gluErrorString(Error);
    		fprintf(stderr, "OpenGL error: %s\n", message);
    	}
    }
     
     
    test->End();
    delete test;
     
     
    SDL_QUIT();

    Thanks

  6. #6
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    893
    It's a typo error.
    Yeah, it happens.

    Well, actually there is no binding being done to any data structure. Can this be a reason?
    Yes it is bound:

    Code :
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1])

    This will simply bin the name of the index buffer, i.e. buffers[1], to the target GL_ELEMENT_ARRAY_BUFFER. If no data is stored in the data store of the buffer object or if the size of the data store is 0 you'll get either nothing, or undefined behavior since the GL doesn't do bounds checks when issuing rendering commands.

    Did you check for errors using glGetError()?

    Code :
    case SDL_MOUSEBUTTONDOWN: 
    {     
        float coords[4];     
        coords[0] = static_cast<float>(button.x);
         coords[1] = static_cast<float>(480 - button.y);
         coords[2] = coords[3] = 0;
         test->UpdateBuffers(coords);
     }

    This seems rather incorrect. You have to assume that the camera is situated at the origin, e.g. (0, 0, 0). Now, if you simply take the window coordinates, e.g. button.x, which can well be something 300 or something (I'm assuming 640x480 pixel, correct?), you'll get a point in world coordinates with an x-component of 300. This point will most likely not be inside the cameras view frustum, unless it's very distant from the origin and the far plane is set accordingly - or you have an ortho projection with correct parameters. The inversion of y is correct in principle though. For vertices (or points in space) you never have a zero w-component because it will simply not yield correct results when doing perspective division - for directions it's the other way around, setting w to 0 here prevents vectors from being translated and altering the w-component of vertices when doing addition or subtraction. For points you can simply set coords[3] to 1.

    Please post your modelview and projection matrix setup.

    ... once I rub the application.
    You should never rub the application.

  7. #7
    Intern Newbie
    Join Date
    Jun 2009
    Posts
    49
    What exactly do you mean by this?

    Please post your modelview and projection matrix setup.
    If this is what you are speaking..??
    Code :
     glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glShadeModel(GL_SMOOTH);
     
     
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     
     
     gluPerspective(45.0f, 640.0f / 480.0f, 0.1f, 100.0f);
     
     
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     glTranslatef(0.0f, 0.0f, -4.0f);

    You should never rub the application.
    I need this feature also.. to erase/delete a polyline..

  8. #8
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    893
    Yes, that's it.

    Ok, let's take this one apart and have a look at the camera properties. First, you have gluPerspective() setup a frustum with a vertical field of view of 45°, an aspect ratio of 640 / 480 and near clipping planes of .1 and 100. Furthermore, since you setup the model-view matrix to translate the whole scene by negative 4 units on the z axis (or the camera by positive 4 units on the z-axis) the camera is at (0, 0, 4).

    The distance is ok, since the points are all within the clipping planes. However, the x- and y-coordinates are not in most cases.

    From the top of my head and with a little trigonometry you can check which points will still fall inside the view-frustum:

    With

    tan(fov / 2) = maxY / cameraDistance

    it follows that the maximum Y coordinate still inside the frustum is

    maxY = tan(22.5) * 4 =approx. 1.657

    Weighted with the current aspect ratio we get the maximum X coordinate

    maxX = maxY * aspect =approx. 2.209

    Ok, so even with your current settings you will obviously only see what's in the range x = [-maxX, maxX] and the range y = [-maxY, maxY].

    Now think about what that means when the user clicks on the screen. If you simply take the window coordinates, e.g. (100, 100), and put that right into the coordinate array, will this point be inside your cameras frustum? The answer should be simple. You'd have to map the window coordinates to some range that will fall into your frustum no matter where the user clicks on the window. A more simple solution would be to use an orthographic projection.

    Try substituting the projection matrix setup with:

    Code :
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 640, 0, 480, 1.0, 10.0);

    This will symmetrically map any point in the range [0, 640] in x to [-1, 1] and every point in the range [0, 480] in y to [-1,1]. Now you can directly use the window coordinates. All you need to make sure is that your points are always inside the clipping range. I chose [1, 10] in this example since your points are all at 4 units distance from the camera and thus always within [1,10].

    BTW, did you check for GL errors with glGetError() yet? It's important to know that all GL commands are completed successfully if something doesn't work as expected.

    EDIT: Where is your viewport setup? You also need to add this code to begin():

    Code :
    glViewport(0, 0, 640, 480);
    Last edited by thokra; 08-22-2012 at 07:54 AM. Reason: Fix messed up code section.

  9. #9
    Intern Newbie
    Join Date
    Jun 2009
    Posts
    49
    Hi,

    Thanks for the wonderful explanation. I did the following changes in my code as suggested by you,

    Code :
    void ctest::Begin()
    {
     
     glViewport(0, 0, 640, 480);
     
     
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     glOrtho(0, 640, 0, 480, 1.0, 10.0);
     
     
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     
     
     
     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
     
     
     numVertices = 0;
     
     InitBuffers();
    }

    After doing this change, still I wasn't getting proper display. Then I added one clear statement in Render function

    Code :
    glClear(GL_COLOR_BUFFER_BIT);
     
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
    glDrawElements(GL_LINES, numVertices * 2, GL_UNSIGNED_BYTES, NULL);

    With addition of this statement, I got a proper white colored window. was more happy. But clicks weren't producing results. Changed

    Code :
    coords[2] = 0;
    coords[3] = 1;

    Still no effect. On proper observation I found out that after 12 number of left clicks, both calls to glBufferSubData functions in UpdateBuffers() produced

    Invalid Value
    error (using glGetError()). That means, just based on the value of 'numVertices' it might have produced error? Or is it that the points are stored and updated but not being displayed? I thought the color may be causing problem. Background is white, and may be line also might have been drawn with which color. So I added two more statements in render function.

    Code :
    glLineWidth(2.0);
    glColor3f(0.0f, 0.0f, 0.0f);

    But this also didn't seem to work. x-(

    So any idea what might be happening?

    Thanks & Regards
    Rakesh Patil

  10. #10
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    893
    Still no effect. On proper observation I found out that after 12 number of left clicks, both calls to glBufferSubData functions in UpdateBuffers() produced Invalid Value error (using glGetError()). That means, just based on the value of 'numVertices' it might have produced error?
    Yes, glBufferSubData() will generate a GL_INVALID_VALUE if offset and size will together will go "beyond the buffer object's allocated data store." You need to make sure that you only add vertices

    Code :
    if(numVertices < 11)
       ++numVertices;

    As soon as numVertices reaches 12, glBufferSubData() will not be given a legal offset and using the constant size of 2 * GLubyte will produce the above error.

    I still found one logic error. I'll think about it and setup my own little test project to test the code myself. I'll be back.

Posting Permissions

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