PDA

View Full Version : glDrawElementsBaseVertex .. GL_TRIANGLE_STRIP .. height-field



CarstenT
12-08-2013, 05:37 AM
Hi all,

The drawing doesn't get any easier than 'doing' a height-field.
I've got close, but still missing the last piece of the pussle.
The data is 1200*1200 vertices in a .. 1200*1200 plain grid with height-values at the position.y.
My graphics-card has a limit of ~1 mill vertices (and do indices), so I've had to split the 1.4mill vertices. After trying different possibilities I've chosen to use glDrawElementsBaseVertex with GL_TRIANGLE_STRIP and to cut up the drawing into 4 calls.
To judge by the visual output it looks as if not all vertices in each call are drawn to the screen.
The calls splits as four broad bands (300*1200) vertices that conveniently are drawn as 300 GL_TRIANGLE_STRIPs. Approximately 1/4'th of the vertices seems to be drawn, but what DOES get drawn is ok and all positioned properly: I get a partly painted square with wide empty gaps in.
Doing only one call (1/4'th of 1.4 mill) does not affect the amount of vertices being drawn.

Kind of stupid question: but that leaves the problem at the VAO/VBO's, doesn't it?
Unless anyone can find the bug in the code:


Load and fill arrays:


const GLuint SRTM_SIZE = 1201;
Vertex3 Vertices[ SRTM_SIZE * SRTM_SIZE ] ;
static GLfloat vPosArray[ SRTM_SIZE * SRTM_SIZE ][4] ;
static GLfloat vNormArray[ SRTM_SIZE * SRTM_SIZE ][4] ;

static GLint Load_1201_HGT(){
//load file,
for (GLuint i = 0; i < SRTM_SIZE; ++i)
{
for (GLuint j = 0; j < SRTM_SIZE; ++j)
{
..... read value "q"
GLfloat p = (GLfloat) q/90 ;

Vertex3 myVert;

myVert.position.x = (GLfloat) j ;
myVert.position.y = (GLfloat) p ;
myVert.position.z = (GLfloat) i ;//

Vertices[i * SRTM_SIZE + j] = myVert ;
vPosArray[ i * SRTM_SIZE + j ][ 0 ] = (GLfloat) j ;
vPosArray[ i * SRTM_SIZE + j ][ 1 ] = p ;
vPosArray[ i * SRTM_SIZE + j ][ 2 ] = (GLfloat) i ;
vPosArray[ i * SRTM_SIZE + j ][ 3 ] = 1.0f ;
............
.......... calculate normals

vNormArray[ i * SRTM_SIZE + j ][ 0 ] = norm.x ;// swap x and z ????
vNormArray[ i * SRTM_SIZE + j ][ 1 ] = norm.y ;
vNormArray[ i * SRTM_SIZE + j ][ 2 ] = norm.z ;
vNormArray[ i * SRTM_SIZE + j ][ 3 ] = 1.0f ;



building indices:


const GLuint marker = 10000000 ;
const GLuint indicesCount = 2*299*SRTM_SIZE + 299 ;
static GLuint Indices[indicesCount] ;

void long4Indices(){
GLuint counter = 0 ;
GLuint lon = 0 ;
GLuint lat = 0 ;
//due to the 'symmetry' the lat = 1201 is skipped
for( lat = 0; lat < 300 - 1 ; lat++ ) // lat iterates 299 times, but involves 2 lines (including +extra)
{// two rows are traversed
for( lon = 0; lon < SRTM_SIZE ; lon++ ) // lon iterates 1201 times
{
Indices[ counter ] = (GLuint) lon + lat * SRTM_SIZE ;
Indices[ counter + 1 ] = (GLuint) lon + (lat + 1) * SRTM_SIZE ;
counter += 2 ;
}
Indices[ counter ] = marker ;
counter ++ ;
}
printf("is indices : %i equal to counter : %i\n", indicesCount , counter ) ; // yes
}




GLuint vao;
GLuint vertexBufferObject ;
GLuint indexBufferObject ;

static void initProgram()
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

//--- SRTM_SIZE * SRTM_SIZE vector of four floats (position or normals)
size_t normalOffset = sizeof(float) * 4 * SRTM_SIZE * SRTM_SIZE;

glGenBuffers(1, &vertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, normalOffset + normalOffset , NULL , GL_STATIC_DRAW);

glBufferSubData( GL_ARRAY_BUFFER , 0 , normalOffset , vPosArray ) ;
glBufferSubData( GL_ARRAY_BUFFER , normalOffset , normalOffset , vNormArray ) ;

glEnableVertexAttribArray(0); // positions
glEnableVertexAttribArray(1); // normals
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)0 ) ;
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)normalOffset ) ;

glGenBuffers(1, &indexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);

glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount , Indices , GL_STATIC_DRAW);
glEnable( GL_PRIMITIVE_RESTART ) ;
glPrimitiveRestartIndex( marker ) ;
}//------------------------------------------------------------------------------


DISPLAY:


....
glDrawElementsBaseVertex( GL_TRIANGLE_STRIP, indicesCount , GL_UNSIGNED_INT , (const GLvoid *)0 , 0 ); //
glDrawElementsBaseVertex( GL_TRIANGLE_STRIP, indicesCount , GL_UNSIGNED_INT , (const GLvoid *)0 , 300*1201 );
glDrawElementsBaseVertex( GL_TRIANGLE_STRIP, indicesCount , GL_UNSIGNED_INT , (const GLvoid *)0 , 2*300*1201 );
glDrawElementsBaseVertex( GL_TRIANGLE_STRIP, indicesCount , GL_UNSIGNED_INT , (const GLvoid *)0 , 3*300*1201 );
....

CarstenT
12-08-2013, 12:06 PM
1197

The image may inspire you to find my error ..
Each draw Call outputs a strip.
Instead of stuffing positions and normals into the same vbo I've added a new so each has their own. It does not influence the output.

The Little Body
12-12-2013, 01:48 PM
Hi all,

The drawing doesn't get any easier than 'doing' a height-field.
I've got close, but still missing the last piece of the pussle.
The data is 1200*1200 vertices in a .. 1200*1200 plain grid with height-values at the position.y.
My graphics-card has a limit of ~1 mill vertices (and do indices), so I've had to split the 1.4mill vertices. After trying different possibilities I've chosen to use glDrawElementsBaseVertex with GL_TRIANGLE_STRIP and to cut up the drawing into 4 calls.
To judge by the visual output it looks as if not all vertices in each call are drawn to the screen.
The calls splits as four broad bands (300*1200) vertices that conveniently are drawn as 300 GL_TRIANGLE_STRIPs. Approximately 1/4'th of the vertices seems to be drawn, but what DOES get drawn is ok and all positioned properly: I get a partly painted square with wide empty gaps in.
Doing only one call (1/4'th of 1.4 mill) does not affect the amount of vertices being drawn.

Kind of stupid question: but that leaves the problem at the VAO/VBO's, doesn't it?
Unless anyone can find the bug in the code:
]

Have you test with 16 bands instead that with only 4 ?

Because the memory needed for to store 1/4 of the 1.4 million (so 360 000 vertices) is perhaps too big for to be entirely stored into your on-board memory ?
(but I have make the computation and this make only something like 60 Mo of data => so, I don't think that the problem can to be here)

And/or the glDrawElementsBaseVertex() call can only handle a limited number of elements ?
(about 65K if it can only deal with 16 bits unsigned integers indices on it's internal circuitry for example => in your example yu use about 90K elements, so about 25K more indiices [and this make only something like 32K max indices with 16 bits **signed** internals indices, so only 1/4 of indices can really to be treated at each call])

What give glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices) and glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices) calls for examples ?

Or perhaps just use glDrawElementsBaseVertex( GL_QUAD_STRIP, ...) instead of glDrawElementsBaseVertex( GL_TRIANGLE_STRIP, ...) calls ?

Can you please post the entire code ?
(I have only tested the building indices code with Indices[ counter ] and Indices[ counter +1 ] commented and this give this :


is indices : 718497 equal to counter : 718497

=> isn't something like (360 000 / 4) * 2 indices = 180 000 indices that we have to handle for each call ?
(if we use quad strips with 2 new indices for each new quad)

CarstenT
12-13-2013, 07:26 AM
Hi The Little Body,
Yes, I tried with 12 bands. The strips got thinner and still only ~1/4'th of the whole band is drawn.
I'm not using 16 bit integers for the indices.
GL_MAX_ELEMENTS_VERTICES and GL_MAX_ELEMENTS_INDICES are both slightly above 1 mill. I think that it's here that the problem lies in the sense that these 'specs' may cover - MAX_ELEMENTS*components - instead. That makes for only 1/4 of the full count when I use 4 component vectors (for position and normal). If the setup works with MAX_ELEMENTS_VERTICES = MAX_ELEMENTS*components, then it's still not enough to split the 'heights' in four tiles .. I would have to split it all way down into 3*3 = nine tiles .. that's small, if I want equal size squares.
I read another post where generating triangle-strips in a geometry-shader had a practical max_input vertices for each strip far lower than 'specs'. I looked forward to test the geometry-shader building TRIANGLE_STRIPs, but the poster expected much more than the 64 vertices he got per generated TRIANGLE_STRIP.
I've never used QUADS .. I don't think that it's more efficient than GL_TRIANGLE_STRIP (in terms of low indices-count). If you glean the indices, you'll notice that I use two rows of vertices to generate the 1200 row long (2400 total vertices) TRIANGLE_STRIP. I've glared a lot on it and found no errors.
Instead I've pondered weather there could be a shortcut by way of loading the heights in a texture, but I havn't been there yet. It could give input components as low as two .. u & v, but it still is no gain if the MAX is with respect to output-vertex-count (it'll still have to be four component out) ;o/
I'll post news when I've tried .. something.
Great to get a response.

CarstenT
12-14-2013, 06:15 AM
I've extracted a 500*500 sub-tile of the 1200*1200 vertices. Neither GL_MAX_ELEMENTS_VERTICES nor GL_MAX_ELEMENTS_INDICES are exceeded. Only the first 1/4 of the vertices are drawn to screen. If I reduce the indices-count in glDrawElements to a few lines, it will be painted without errors. Making a 'glBeginQuery(GL_PRIMITIVES_GENERATED, myQuery ) I get a number that averages to the indices-count input. .. (I'll take a look at the GLsizei type), but aren't a GL_PRIMITIVE something else than an index? ..
1200
The 'camera' 'looks at' the center of the tile (250,250).

CarstenT
12-14-2013, 12:27 PM
Solved,

glBufferData() wants an indices-bytesize and not an indices-count ...
1201
Sometimes I wished that my mood wouldn't be that much dependent on these outputs. Anyway, the sun shines now ;o)