Using VBO to draw line string

2019-08-09 00:36发布

问题:

I am able to render the below points using the VBO and shaders in OpenGL 4.x.

    typedef struct Point
    {
        double x,y,z,w;
    }Point;

    std::vector<Point>vPoints;

    glBufferData(GL_ARRAY_BUFFER,  vPoints.size()* sizeof(Point),  &vPoints[0], GL_STATIC_DRAW);
    glVertexAttribPointer(0, 4, GL_DOUBLE, GL_FALSE, vPoints.size()*sizeof(GLdouble), (GLvoid*)0)

How do we specify VBO to draw a line string using below variables

    typedef struct LineString
    {
     vector<Point> vPointList;

    }LineString;
    vector<LineString> vLines;
    glBufferData(GL_ARRAY_BUFFER,  ????,  ????, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 4, GL_DOUBLE, GL_FALSE, ????, (GLvoid*)0)

I tried below setting and it does not seem to work

   glBufferData(GL_ARRAY_BUFFER,  vLines.size()* sizeof(LineString)*sizeof(Point),  &vLines[0].vPointList[0], GL_STATIC_DRAW);
   glVertexAttribPointer(0, 4, GL_DOUBLE, GL_FALSE, vLines.size()*sizeof(GLdouble), (GLvoid*)0);   

I thought I almost got there with below code for VBO creation

         for(int i=0;i< vLines.size();i++)
           pt_count += vLines[i].vPointList.size();

  fprintf( stdout, "Total point Count: %d\n", pt_count);    
  glBufferData(GL_ARRAY_BUFFER, pt_count * sizeof(Point), nullptr, GL_STATIC_DRAW);

size_t start = 0;
for(int i=0;i< vLines.size();i++)
{
    size_t v_size =vLines[i].vPointList.size() * sizeof(Point);
    glBufferSubData(GL_ARRAY_BUFFER, start, v_size, &vLines[i].vPointList[0]);
    start += v_size;
}

if(start == pt_count * sizeof(Point) )
{
    fprintf( stdout, "INFO: %s\n", "Same count");   

}

glVertexAttribPointer(0, 4, GL_DOUBLE, GL_FALSE, 4 * sizeof(GLdouble), (GLvoid*)0);

and drawing call.I noticed that the last point of first line is getting connected to first point of the line.

    glDrawArrays(GL_LINE_STRIP, 0,pt_count);

回答1:

glBufferData

For glBufferData, the total size of the data in bytes is required. In addition a consecutive memory segment is required, which a vector<vector<T>> does not provide. Technically, each inner vector has it's own memory segment where the data is stored, which makes it impossible to upload the data at once. The only way that comes to my mind is to first calculate the total size like this:

size_t pt_count = 0;
for (auto& v : vLines)
    pt_count += v.vPointList.size();

The allocate GPU memory, but without uploading any data

glBufferData(GL_ARRAY_BUFFER, pt_count * sizeof(Point), nullptr, GL_STATIC_DRAW);

The final step is then to upload all the points step by step:

size_t start = 0;
for (auto& v : vLines)
{
    size_t v_size = v.vPointList.size() * sizeof(Point);
    glBufferSubData(GL_ARRAY_BUFFER, start, v_size, &v.vPointList[0]);
    start += v_size;
}

But if you can change the data layout in the C++ code, I would strongly advice you to store all the points in a consecutive memory segment in the first place.

glVertexAttribPointer

The fifth parameter of glVertexAttribPointer tells OpenGL how much offset is between consecutive vertices in the buffer. So think of a buffer like this:

        | x | y | z | w | x | y | z | w |
stride  |-------------->|-------------->|

this is needed, since is is possible, that between two vertex entries additional data is stored. In your case, the offset has to be 4 * sizeof(GLdouble), or since the data is tightly packed, 0:

Specifies the byte offset between consecutive generic vertex attributes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. The initial value is 0. reference



回答2:

Could solve this by making use of glMultiDrawArrays()..Found the solution by correctly specifying the index positions.The code looks like below after fixes.

//globals
size_t pt_count = 0;
GLint  *startIndices;
GLint *endIndices;
GLint nLineCount;


//create the VBO
for(int i=0;i< vLines.size();i++)
    pt_count += vLines[i].vPointList.size();


  startIndices= new GLint[vLines.size()];
  endIndices= new GLint[vLines.size()];


  fprintf( stdout, "Total point Count: %d\n", pt_count);    
  glBufferData(GL_ARRAY_BUFFER, pt_count * sizeof(POINT), nullptr, GL_STATIC_DRAW);

    size_t start = 0;
    size_t firsts=0;
    for(int i=0;i< vLines.size();i++)
    {


        size_t v_size =vLines[i].vPointList.size() * sizeof(POINT);

        glBufferSubData(GL_ARRAY_BUFFER, start, v_size, &vLines[i].vPointList[0]);
        start += v_size;

        startIndices[i]=firsts;
        endIndices[i]=vLines[i].vPointList.size();
        firsts+=endIndices[i];
        fprintf( stdout, "start: %d\n",startIndices[i]);
        fprintf( stdout, "size: %d\n", endIndices[i] );

    } 


    if(start == pt_count * sizeof(POINT) )
    {
        fprintf( stdout, "INFO: %s\n", "Same count");   

    }

  glVertexAttribPointer(0, 4, GL_DOUBLE, GL_FALSE,  4 * sizeof(GLdouble), (GLvoid*)0);

  nLineCount=vLines.size();
  vLines.clear();
 //Draw the lines
 glMultiDrawArrays(GL_LINE_STRIP, startIndices,endIndices,nLineCount);