PDA

View Full Version : VBO with dynamically changing number of points..!!



rakeshthp
08-21-2012, 12:08 AM
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

thokra
08-21-2012, 02:19 AM
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!



// 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.



// 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:




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.

rakeshthp
08-22-2012, 12:20 AM
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

thokra
08-22-2012, 01:04 AM
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?

rakeshthp
08-22-2012, 03:50 AM
What went wrong?

It's a typo error. An extra 'F' has come here



glBufferSubData(GL_ELEMENT_ARRAY_BUFFFER, indexOffset, sizeof(indices), indices);

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


#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


#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


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

thokra
08-22-2012, 04:17 AM
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:


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()?



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. ;)

rakeshthp
08-22-2012, 06:19 AM
What exactly do you mean by this?


Please post your modelview and projection matrix setup.

If this is what you are speaking..??


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.. :)

thokra
08-22-2012, 07:33 AM
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:



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():



glViewport(0, 0, 640, 480);

rakeshthp
08-22-2012, 10:29 PM
Hi,

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



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


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


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.


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

thokra
08-22-2012, 11:00 PM
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



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.

thokra
08-23-2012, 02:24 AM
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:



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:



#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.
855
HTH!

rakeshthp
08-23-2012, 02:35 AM
Oh that's very kind of you. Thank you very much. I'll try that and get back to you. :)

rakeshthp
08-24-2012, 12:11 AM
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


glBufferSubData

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

thokra
08-24-2012, 03:01 AM
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:



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 (http://www.arcsynthesis.org/gltut/).

rakeshthp
08-24-2012, 04:01 AM
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. :)

thokra
08-24-2012, 04:33 AM
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:



#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.

rakeshthp
08-24-2012, 05:03 AM
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 :) ;)

thokra
08-24-2012, 05:59 AM
[..]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:



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:



// 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:



#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 ...