Using two vertex arrays to render a single series of vertices?

I’m trying to optimize my terrain renderer by separating the terrain itself into a series of segments. Each segment is the same size and shares with other segments the x and y coordinates of its vertices as well as texture coordinates. I was thinking that I could set up a vertex array containing that data and a separate array containing the z coordinate, plus colour and normal vectors. The second array would be unique to each segment, of course. The x, y, and z coordinates would be assembled in a vertex program. The result would be that I’d be saving 16 bytes of data per vertex, per segment beyond the first. Can this be done with VBO?

I can’t comment on if VBO could handle what you are proposing (somehow I doubt it) but I can suggest that what you want could probably be implemented using a displacement map & vertex programs.

Of course you need to ask the question of “why?”. RAM is dirt cheap in large quantities and terrains don’t have to be too detailed to be effective. And you’re not going to get any benefit from doing this with respect to the card…

You can’t store X, Y and Z in separate arrays, but you can create one array with 2D vertex coordinates (X, Y) and another one with 1D texture coordinates that represent the height. Then you can piece them together in a vertex program.

– Tom

rgpc: Yeah, it’s true that video cards have tonnes of RAM, but I’ve run into a 32 MB limit on vertex arrays. I’ve contacted ATI and it appears that this is how it’s supposed to be working. On the subject of displacement maps. . . um. . . how exactly would I do that?

Tom: Yeah, I should have explained it better. I was planning on the first array using 2d vertex coordinates, as you suggested, and in the other array using a 1d “attrib” (glVertexAttribPointerARB). Basically the same thing as what you mentioned. How would I do this with VBO, though? Would I just bind the two arrays, one after the other?

32Meg! That must be some terrain…

I think Tom’s idea is a good one and should be relatively simple to implement (Plus it’s likely to be more accurate than a displacement map).

Presumably you problem is that you can’t allocate more than 32Meg of VAO/VAR/VBO? Are you sure you need all 32Meg “on the card” at once? Really you only need the segments of your terrain that are visible and as you move from segment to segment, you can remove data and add new data as it’s needed. This’d keep the total data on the card at a minimum but you can go above the 32Meg limit because the majority(?) of your data is stored in system memory.

If you implemented both systems then you could store your X/Z on the card (permamently) and just transfer the Y (1D tex coords) when you need to. The only problem is if you plan to support cards that don’t have Hardware based VP’s.

If you re-use the X/Y components, then that means that you change the modelview matrix between each block. This will guaranteed lead to cracks/pinholes, unless you add flanges on the side of the terrain (which I don’t think is a great solution, but it’s better than nothing).

How would I do this with VBO, though? Would I just bind the two arrays, one after the other?

The API mechanism escapes me for the moment, but the idea is that, while a particular buffer is bound, any glpointer calls will reference offsets into that buffer. If you bind a buffer, call glpointer, bind another buffer, then call a different glpointer, the first call’s location will still be referenced based on the first buffer. In that fashion, you may use 2 (or any number) of separate buffer objects for separate glpointer calls.

If you re-use the X/Y components, then that means that you change the modelview matrix between each block. This will guaranteed lead to cracks/pinholes, unless you add flanges on the side of the terrain (which I don’t think is a great solution, but it’s better than nothing).

Actually, there is a way to handle this (I’m already planning to use this same stratdgy, and I asked about this earlier).

If the local visible terrain is, say, a 5x5 grid, where each section is a piece of terrain, what you can do is create 25 vertex buffers: one for each of the X/Y components of the segments. These buffers are purely static; they never change.

You will, also, need 25 buffers for the heights. Each time the camera goes into a new segment, you must shift which grid segment the heights are attached to, and shift the scene’s world-space position appropriately (so that the camera always remains in the center grid location).

Done and done.

Originally posted by rgpc:
[b]32Meg! That must be some terrain…

Presumably you problem is that you can’t allocate more than 32Meg of VAO/VAR/VBO? Are you sure you need all 32Meg “on the card” at once? Really you only need the segments of your terrain that are visible and as you move from segment to segment, you can remove data and add new data as it’s needed. This’d keep the total data on the card at a minimum but you can go above the 32Meg limit because the majority(?) of your data is stored in system [/b]

Yeah, that’s true. I have a 3x3 grid of 256x256 terrain segments rendering each frame. I decided this should be my minimum for a clipping plane of 256 units. This isn’t even all of the terrain, either. Once one exits one segment, three are deallocated from video memory and remain cached in system memory, and 3 more segments are generated and loaded into video memory. The three that were just deallocated remain cached just in case one decides to suddenly turn around. The amount of time it takes to generate the vertices and upload to video memory is small compared to the time it takes to generate a terrain segment from scratch (I use a fBm algorithm).

Since I’m turning this into a ROAM based terrain (well, trying), each segment is actually made up of an 8x8 grid of 33x33 sub-segments. With a 44 byte vertex (I can probably reduce this by using UCHARs for colour), that’s 2.9 MB per segment, for a total of 26.3 MB. If I were able to use the method Tom describes, I could shave that down to 1.9 MB per segment and a total of 16.8 MB, leaving plenty of textures and other meshes.

Originally posted by Korval:
The API mechanism escapes me for the moment, but the idea is that, while a particular buffer is bound, any glpointer calls will reference offsets into that buffer. If you bind a buffer, call glpointer, bind another buffer, then call a different glpointer, the first call’s location will still be referenced based on the first buffer. In that fashion, you may use 2 (or any number) of separate buffer objects for separate glpointer calls.

Ah. . . thanks! I had been worrying that binding a new buffer would cancel out any previous gl*pointer calls.