Texturing A Degenerated Triangle Mesh


Zotty

Member
Joined
Aug 1, 2004
Messages
205
Location
Netherlands
Website
www.schijf.org
I've been playing around with 3D terrain rendering using a degenerate triangle mesh stored in a VBO. However I cannot figure out how to texture it. By now I'm starting to wonder if it's even possible, so time to call in the troops. Is it possible or should I go back to a non-degenerate triangle mesh?
 
In what way did you degenerate it?
The way apple recommends it?
- What primitive type do you use now?
How do you want to texture it? Tiled? One map for all?

... More infos please.

//Edit: But in theory you can step on one position with every primitive type and change the UV to your liking. The degenerated triangles should be dropped in an early-out test.
 
Originally I planned on porting the Settlers, but this quickly turned into writing something from scratch. So imagine a tiled Settlers/Widelands landscape, only 3D instead of 2D. Screenshot.

The mesh is build like this:
Code:
	int currentVertex = 0;
	int currentIndex = 0;
	GLushort w = m_mapWidth + 1;
	int x(0), y(0);
	for (y = 0; y < m_mapHeight + 1; y++) {
		for (x = 0; x < m_mapWidth + 1; x++)
		{
			// vertices
			terrainBuffer[currentVertex]     = static_cast<GLfloat>(x);
			terrainBuffer[currentVertex + 1] = static_cast<GLfloat>(map.height(x, y));
			terrainBuffer[currentVertex + 2] = static_cast<GLfloat>(y);
			currentVertex += 3;

			// indices
			int n = y * (m_mapWidth + 1) + x;
			if (y < m_mapHeight && x < m_mapWidth) 
			{
				// Face one
				indices[currentIndex]     = n + w;
				indices[currentIndex + 1] = n + 1;
				indices[currentIndex + 2] = n;

				// Face two
				indices[currentIndex + 3] = n + 1 + w - 1;
				indices[currentIndex + 4] = n + 1 + w;
				indices[currentIndex + 5] = n + 1;

				currentIndex += 6;
			}
		}
	}

Rendering is done using draw elements: glDrawElements(GL_TRIANGLES, m_mapWidth * m_mapHeight * 6, GL_UNSIGNED_SHORT, 0);

Don't know what Apple recommends, but I've been reading the docs provided by the Khronos OpenGL ES SDK. I started out using a triangle strip, but as far a I understood, indexed triangles are faster.

Still new to opengl, so be gentle ;)
 
What you are supposed to use are indexed triangle strips. However, with a terrain like that you'd have to split it into smaller segments anyway. I'm also wondering how it is going to be textured.
Bascily you could generate the mesh like this:

1270400545-1.jpg


The black lines show those you would use in the index buffer, red is what will be rendered, green circles mark degeneration points.
Black numbers show the index number, the upper right is the index buffer (numbers with 2 digits are split into two columns.

Basicly the generation should be fairly easy to do. When you reach the end of a column just add another index to the index buffer with the previous (if y = ymax and if x mod 2 = 1) / next (if y = 0 and x mod 2 = 0) vertex index (Making a triangle like AAB or BCC which should be early-out'ed).


I hope I didn't fuck that up somewhere (but its likely ;) )


You would have to use a stretched texture over the map as the vertices share the same U/V. However, you could also put the index into the vertex struct and use the fragment shader to tile it again if you want texture per tile.
 

Attachments

  • 1270400545-1.jpg
    1270400545-1.jpg
    4.9 KB · Views: 51
Thanks, back to strips it is. Didn't use indices during that test, but shouldn't be that hard to change :)

As for the textures I keep running into that state machine. Having to bind a single texture each time is making this more complex than I had hoped. But it's a interesting challenge. Was thinking of building the VBO sorted by vertices per texture, e.g. add all vertices for texture A, then alle vertices for texture B and so on.
However this still leaves me puzzled on how to add the texture coordinates when skipping vertices using indices. When skipping a vertice there's no point to attach a texture coordinate to. Or I'm missing something here?

Using 1 large texture would be a lot easier, but it would be too big. Let's say we have a map of 128x128 tiles, using a 64x64 RGB565 texture per tile. That would add up to 128MB uncompressed. I could reduce the level of detail by taking smaller textures per tile, but since I want to be able to zoom in pretty close, that might be a problem.

Perhaps a completely different approach would be better, but atm I'm just experimenting.

Btw, nice drawing! Got to love whiteboards ;)
 
You could upload your different textures for different samplers. Then you either include the tile type in the vertex struct (meaning each vertex would actually be the center of a quad in the game probably).
You could also just use another lookup texture which is 128x128, one component (or 32x128) with 4 components - each pixel / component would then represent the tile type - you would then multiply the texture amount by the tile type.

I wouldn't go with the "one VBO per tile type" because you loose blending between tiles AND you would also make LOD streaming harder. Its probably best to seperate the landscape in smaller tiles (depending on the zoom too probably? keeping them in memory so you can either do 3x3 or 9x9 tiles for example) so you can skip those which are not in the viewfield. Then you include a tile type per vertex and upload a texture atlas for the tile types (which you can keep in memory all the time), and possibly some perlin noise to be used in transitions so they are not too linear (Possibly a special map to handle mixing (use 4 components, every component defines how nuch each vertex contributes, making it possible to have sharp transitions between rocks and sand and a "loose" transition between grass and sand))


- however, this would require shaders.
 
This has given me a lot of food for thought and will probably take some time to fully comprehend and try out. Also if I understand you correctly GLES2 offers more possibilities for this specific problem. I've been sticking to GLES1 for no apparent reason, so I might as well jump on the GLES2 train. One of the things on my todo list is using shaders for water effects, so maybe the same technique can also be used for grass/rock/sand/etc.

Using a texture atlas crossed my mind, but I read they can cause adjacent texture bleeding in this kind of usage.

Btw, in my previous post I didn't mean separate VBO's, but 1 VBO. I made the mistake of thinking the code still used glDrawArrays where you can use offsets. glDrawElements does not offer that, so that thought was kind of pointless.
 
1270671221-1.jpg


This is a first concept I came up with. Depending on the fact if you need square tiles or not this approach would get the mesh closer to the camera (which has exact squares again, smoothed and then flattened (only the top one) for buildings etc.)
The one in the back is a smoothed version without any post-steps.
Both can be freely textured on a per tile basis (with the one in the back being slightly uneven). I used only every second tile as you get some nasty walls if you set a different height on two tiles next to each other (IF you want this you either have to have lots of degenerated triangles (which are probably not removed as they are only 0 in size but they don't share the same vertex) OR you must come up with another clever step in your mesh generation code)

I have started to make a small demo using my approch (Only use every second tile (I'll possibly reduce size of the steps between so a grid could be used if necessary - fine for buildings etc. and still visually appealing), catmull-clark to smooth and subdivide it, flatten out necessary fields).
I already made transition maps for some materials so they can be mixed on the fly. I have also seen a clever approach in a qualcomm paper recently which said that most modern chips (possibly their own ;) but I'd just like to try it on the SGX) can use 3D textures / texture-stack instead of atlases. I'll load up to 4 different materials (64x64 pixel textures) at once into memory, then a single (possibly 2) texture with 4 channels for the transitionmaps.
A second shader will be used in a second step which would render transparent water (not sure about refraction yet, but possibly reflection to some degree).
Due to the nature of catmull-clark and the shape of the grid any tile can be changed on its own so a small change in the terrain doesn't affect other tiles (unless you remove the tile between the changeable tiles).

Thats it by me for now, if you have any questions you can feel free to keep asking in this thread.
(Don't expect my demo too soon as its going to be in my Pandora SDK which has been delayed numerous times before ;) )
 

Attachments

  • 1270671221-1.jpg
    1270671221-1.jpg
    4.9 KB · Views: 54
Zotty said:
Btw, in my previous post I didn't mean separate VBO's, but 1 VBO. I made the mistake of thinking the code still used glDrawArrays where you can use offsets. glDrawElements does not offer that, so that thought was kind of pointless.
the reason glDrawElements does not take an explicit offset argument is that it does not need one. in contrast to glDrawArrays which works with pre-established arrays, glDrawElement is the array itself - it takes it's own 'array' pointer and does not need offsets in other arrays.

so let's say you had a VBO of indices which generally encompasses your whole mesh. if you wanted to draw a subset of that you'd just need to do glDrawEments(mode, subset_elemet_count, type, offset_to_subset_of_indices)
 
Last edited by a moderator:
JayFoxRox said:
I have also seen a clever approach in a qualcomm paper recently which said that most modern chips (possibly their own ;) but I'd just like to try it on the SGX) can use 3D textures / texture-stack instead of atlases.

Do you have a link to this paper? This is an interesting idea; I take it so long as your r texture coordinate is whole you won't have to worry about ending up between textures.

Unfortunately your textures would still be limited to all being the same dimensions.
 
Last edited by a moderator:
http://www.qdevnet.com/dev/gpu/tools - "Adreno 200 Performance Optimization: OpenGL ES Tips and Tricks":

http://www.qdevnet.com/sites/default/files/private/tools/Adreno200PerformanceOptimizationOpenGLESTipsAndTricks_March10.pdf


Use Texture Stacks:
Texture stacks are a unique GPU feature that can assign an array of
textures to a single texture stage. Texture stacks can be used to batch
draw calls together and minimize state setting between draw calls. In other
words, 3D textures can be used to minimize texture cache thrash for
materials that have multiple textures. Each layer of textures in the 3D
texture can be a separate texture needed by the material. The fragment
shaders would have to be written such that the texture Z coordinate is
chosen to select the appropriate texture.

Of course you are bound to specific texture sizes. But for a terrain like this, it's insane!
With my approach you can LOD stream much easier than I said before by just changing the stride (not tested yet, but should work in theory) and for all textures you can design it like this:
A stack of 64x64 textures which has 4 textures (POT - not sure if a value like 5 would work):
- Rock / Sand [Triangular shape]
- Mud / Rocks [Triangular shape]
- Detail and dirt-maps
- Transitions (one transition lookup per channel, since you only need about 2 lines you can easily put other stuff here too!)

Compress these using ETC or use 16bpp and you end up with very few memory (Should be equal to max. 32768 Bytes which could also be a single 128*128 16bpp texture), probably good performance and no additional state or texture switches for the terrain.
All you have to do while rendering would be some simple culling tests. I'd expect this to be the best way to render terrains on the SGX. - But expirements still have to proof this. (I also plan to try out the shadowing iq used in elevated).

And just to update: this is my grid so far

1270747739-1.jpg


- Tri-strip with (width+1)*(height+1) vertices and width*(height+1) indices. It's enough to upload a single index in theory instead of x,y so you could optimize the vertex structs further (but that would put more load on the (probably: fast) vertexshader).
 

Attachments

  • 1270747739-1.jpg
    1270747739-1.jpg
    4.9 KB · Views: 61
on the subject of terrain texturing - most of the clever procedural alogrithms i've see are projection based.

for instance, triplanar mapping for tile-able ground patterns, combined with cube mapping for ambient irradiation (and an ambient occlusion map, naturally).

the idea of triplanar mapping is that the angle of the surface normal at a point determines the amount of texturing there. the unit-length normal vector is decomposed into x, y and z components, and the squares of those are taken as wights to the pattern textures (as the sum of those equals 1 for a unit-lenght vector). the surface point's coordinates define the tiling along the respective planes - x/y for the z, y/z for x, and x/z for y pattern textures, respectively. works particularly well with rocky/trenchy terrains (particularly ones made of metaballs).
 
Back
Top