Game Development Community

Terrain Texturing

by Luigi Rosso · in Torque Game Engine · 11/02/2002 (6:26 am) · 13 replies

I'm curious as to how Torque does its terrain texturing.

It seems like some sort of splatting algorithm yet the realtime painting suggests otherwise.

Is the blending done using GL_INTERPOLATE_EXT? I'd think not because it looks like more than two textures are blending together in different places (or am I wrong?). If it's more it'd need to be something like env_combine, or env_add from the arb extensions correct?

Thanks :)

#1
11/02/2002 (8:18 am)
I'm not the best with the 3d code, but I think its done with asm in the blender files.
#2
11/04/2002 (10:29 am)
All of the coordinates for the terrains (vertices/textures) are stored in mXFVertices. It then draws a terrain block by calling renderXFCache(). renderXFCache() is a loop that calls glDrawElements() which draws the terrain square through vertex arrays, whose data is specified in the .ter file. Hope that helps.
#3
11/04/2002 (6:25 pm)
That's something, thanks :) But not quite what I was wondering about...

I'm curious as to how the blending of the terrain textures is done. Torque has (I believe) the only terrain engine which allows for texture splatting modification (via the terrain editor) in realtime. It looks wonderful and beats most terrain texturing systems out there, I'm just curious as to how it's done to see whether it's possible to increase the optimal number of textures used on the terrain (or speed things up via multitexturing).

Thanks for the replies though :)
#4
11/05/2002 (8:24 am)
Got off my lazy butt and read through some of the code!

Here's what I understood:

What handles the blending of the terrain tiles is the blender class. It generates a blend map (alpha map) for every texture on the terrain. The alpha map and the texture's rgb are blended via hardware (using multitexturing) and rendered in a single pass. That means that if your terrain has two textures it'll take 2 passes, 3 textures 3 passes, 4 textures 4 passes... The alpha map for each texture is the same size as the terrain (so 256x256, 8 bits...roughly 65.5 kb per blend map).

There's obviously lots more to it, but that's the basics, it seems correct, can anyone confirm? It's very well optimized (even though whoever commented it didn't seem to think so :)) considering it creates and recreates the blend textures dynamically.
#5
11/06/2002 (3:42 pm)
You are close. The terrain texturing methodology is heavily spread across the blender class and the terrainRender structure. The terrainRender batches up terrain squares that share the same texture LOD. And these are then rendered using a blended texture.

To fully explain this is difficult so bear with me. The terrain is 256x256 squares big. The terrain has up to 8 textures available for blending onto it. However the supplied 256x256 bitmap textures are actually mapped over a chunk 4x4 squares. I think this was done to conserve memory, and CPU cycles doing the blending. The terrain also contains 4 other mipmap levels of the base textures, each being manually calculated at terrain data load time. Each 4x4 chunk has blend weighting values for each of the eight base terrain textures. So the blender will generate a blended bitmap based on the these weights, however for some reason the resultant blended bitmap is only 128x128 pixels (one day I will analyse that code to figure out why). Now the terrain renderer also does some funky texture LODing. The texture LOD process ends up with varying sized chunks from 4x4 to 64x64 squares. Where 4x4 chunk has the appropriately blended result (based on the blending weights) of the 8 base terrain textures mapped across it; A 8x8 chunk is actually four 4x4 chunks, and as such is mapped with a texture that is the combined result of concatenating the blended result of the next mipmap level of appropriate base textures for each of the component 4x4 chunks.

An example might help. An 8x8 chunk, is made up of four 4x4 chunks s1, s2, s3, and s4 in the following arrangement.

+----+----+
| s3 | s4 |
+----+----+
| s1 | s2 |
+----+----+

The blender output texture will be 128x128 which the renderer will UV map over the entire 8x8 chunk. The blender will blend the appropriate 128x128 versions (the next mipmap level) of the 8 base textures for the 4x4 chunk s1, and will store the result in the 64x64 pixel section of the result texture that will cover s1 when renderer. It does this for each of the 4x4 chunks. So the result texture is 128x128 pixels containing the 64x64 pixel versions of the blended 128x128 texture used to texture each sub chunk.

This process is the same for the next three levels of texture LOD. At 64x64 squares, the chunk's 128x128 pixel texture is made up of 16x16 sub-textures each being the 8x8 pixel version of the blended result of the component 4x4 chunk's texture.

Most of the above is off the top of my head, as I am not in front of the source at the moment. So please excuse any inaccuracies.

I am currently working on upping the terrains texture detail myself, and have aceived a 2 fold increase in detail, and am close to a 16 fold. But in the process I have broken the geometry LOD so my FPS is not good. But once I nail the texture detail increase I will fix the LOD.

I hope this all helps.
#6
11/06/2002 (4:00 pm)
Very interesting! Thanks for the detailed explanation.

I read a post Bryan Turner posted on the OpenGL.org forums once which described something similar (I think Charles Bloom also mentioned it in the GDA list). Basically it's applying a quadtree lod algorithm to textures. So for each leaf you have a texture which is the highest resolution, then one step back up the tree you have a texture of the same resolution but which covers all four leafs...this keeps going all the way up the subtree. Since the blended base textures are done in hardware, I'd assume this precision would only really be needed for the alpha maps (minus the mip mapping of each blended texture).

I was thinking of performing the blending of multiple textures in hardware (for cards which can support it). Instead of performing a single texture+blend in one pass, you could probably get away with 2 textures and their respective blends on most cards (have to try to confirm, I'm not 100% sure on that due to needing 4 texture units).

I'd assume the blender needs to reweight all the other textures when a user is texture painting (as each tile must sum to no greater than 1 full alpha value).

Also the lightmap seems to be on the last pass as you can paint the terrain without mucking up the lightmap.

Very interesting conversation, thanks for taking the time to write that up.
#7
11/06/2002 (6:01 pm)
Yep spot on, mind you I think you put it far more precisely and concisely than I did. The terrainRender uses a quad tree to provide both texture LOD and basic geometric LOD. The output of the quad tree traversal is a bunch of 4x4 chunks. The renderer then takes the chunks and applies further geometry LOD when it tessellates them.

Since I am playing with the terrain rendering at the moment I am interested in your ideas. I am a complete "gumby" (cultural reference - inferring naive amateurism mixed with a splattering of stupidity) when it comes to texturing in general. Particularly the intricacies of D3D/OGL blending methods, hardware support blending, and to a degree multipass v single pass texturing.

Also I seem to have broken the lighting in my mission, statics light but the terrain does not. I ran the original render code and the terrain lighting still did not work so I assume it is a bad parameter somewhere in the mission file. This leads me to comment on the terrain lighting system. I had a quick look at the code when I realised it was not running, and I got the impression that the lighting is also dispersed a little. Although I did not get a full understanding of the different sections interaction; As I recollect the blender "blend" function does some lighting after creating the blended texture, and the renderer in "renderChunkOutline" does some lighting as well. It would be interesting to get your take on this, if just for my own education.

Have fun.
#8
11/06/2002 (6:20 pm)
please forgive my highjack of this topic :(

is their a way to dump the completed blended texture to a .png file or into memory to be used in a gui control?

I have seen the map hud resource, but that is just a height map that has been grayed or gradiented. I have also seen the guiterview resource, but that loads every freaking thing in the world ;) you get waterblocks, interiors, players et al. I just want the terrain ;)

-Ron
#9
11/06/2002 (8:31 pm)
If you are looking for a single texture that covers the entire terrain, I would suggest that you might not want it. By my calculations that texture would be 8192x8192 pixels. You see that a 128x128 pixel texture covers a 4x4 square chunk of terrain. Since the actual texture used is distance based the previous statement is for terrain directly at your feet or near by. If you wanted the texture that would be used at the lowest detail, say visible from 1000+ units in the air, it would only be 512x512 pixels.

In any case that is all theoretical since there is no existing way to get access to a whole terrain texture at any distance. You would have to write something.

If there is a different texture you were after, speak up maybe there are ways.
#10
11/06/2002 (9:23 pm)
I thought I had seen another post from you on this topic, so I looked it up. Indeed you do want a texture representing a top down view of the textured terrain.

I don't think that there is a stock way of doing it, but I might have a few suggestions of codeable options. You could call the blender "blend" function for every 64x64 square block on the terrain. And this would give you 16 128x128 pixel textures. See the terrainRender "buildBlendMap" (I think that is the name) function to see how the renderer uses the blend function. You could look at the terrainBlock code that generates the mipmaps of the base textures (can't remember the name of the function and I do not have access to the code right now) to reduce the 16 textures to 64x64 pixels. Then stich the textures together to get a single 256x256 pixel texture representing a top down view of the terrain. This should only need to be done once, unless lighting (dynamic lights) needs to be properly represented.

Hope that is useful.
#11
11/07/2002 (3:46 am)
Ron,

the problem with what you wanted to do, simply dump the blended texture, isn't possible as the texture is actually blended in hardware (unless I'm totally out of it), what the blender does is create the mip maps and the alpha maps.

I read in a post (I believe by Tim Gift) that Tribes 1 did something similar for their command view map. What they did was set the camera up in the sky, look down and render to a texture instead of the frame buffer.

I've seen some dev snapshots illustrating realtime implementations, you could simply use their implementation to generate the rendered texture. You'll probably want to kill fog and toy with the perspective a bit. You'd probably best do this on map load time.

This is a far simpler (implementation wise) method than Jason's, but his will probably yield a better result.

An even simpler approach would be this:

Render the whole texture on one large quad (I'm not sure if the entire texture is stored anywhere, you may have to do some stitching anyway) orthogonally and save it to a texture. Simply render it with all the passes needed for all the textures and their blend maps + the final lightmap and it should look decent (especially with the lightmap).


Jason,

my biggest problem with implementing modifications like these is all the tracing I need to do within the Torque to see what the render code is currently doing. It's well written, laziness and lack of time due to work has much to do with it on my side :)

I've thrown together a simple implementation of Willem H. de Boer's GeoMipMapping to attempt a similar quad tree texturing approach (I've been faster at doing that then at tracing through Torque, again simply due to my situation). I want to see how many textures I can get rendered on my Geforce2 on a single pass and see how significant of a performance increase it would actually provide.

I was also thinking of introducing Torque terrain to nvidia's vertex array range but I'm going to try an implementation on my own first just to see what the caveats are.

I haven't taken a good enough look at the lighting to comment. Adding textures doesn't sound like it should disturb the lighting as it 'should' be a one way relationship. At the most the lighting could disturb the textures.

Try using a clean from CVS mission file to see if that works ok.

Sorry for not being much help, I'll take a closer look at the blend function later on to see what's going on with the lighting.
#12
11/07/2002 (1:31 pm)
Luigi,

All I have done to date is reverse engineer the terrainRender to try and effect greater texture resolution. I find it impressive that you have enough knowledge to sit down and implement a new renderer into Torque (man at computer screen tips his hat). I know the basics, quadtrees, octtrees, ROAM and other LOD basis, I have heard of geomipmapping. In my endeavours I have managed to attain most of my initial goals. I now have a renderer that is heavily based on the original code, but which textures each individual terrain square with the 128x128 pixel blended textures. I want to re-establish the LOD, as currently the entire terrain is rendered as two triangle quads for each visible square. On my test P4 2.4G GF4 machine the terrain renders at reasonable FPS, but I know why LOD is used and think it is still needed.

When the LOD is back I will put this code up for others to review/use/"tear to shreds". In addition to my desired changes, I have removed all the "test" code from the renderer, culled out the unused variable and functions, and added a splattering of comments. I think I might write up all that I now know in some form of tutorial, in case other want to test the terrain rendering water.

On the texture blending side. I might be mistaken, but I think the blending is all done on the CPU. The terrain renderer calls the blender "blend" function, which returns a bitmap of the blended texture. The renderer does two or three passes of the vertex buffer. If there are enough TMUs, it uses the returned blended texture as the base texture and the detail texture as the second texture, and in a second pass does the fog. If only one TMU is available, it does a pass for the blended texture, a pass for the detail texture, and one for the fog. Also the lighting get done some where (I must admit I tend to skip over any obvious lighting code at the moment) but I recollect that it may be a vertex buffer render pass as well. The blender has both asm and C versions of the blend methods. I have not analysed the asm to see if it is exactly the same as the C.

I think that the current LOD system does geomipmapping, but I could not give you the name of the system being used. Basically the list of 4x4 square chunks that come out of the texture LOD quad tree have an indication stored in them as to their suitability for further LOD action. When they get tessellated the renderer decides (based on the LOD action indicator "subDivLevel") whether to tessellate the chunk as a single quad, sub divide it once and tessellate each of the 4 quadrants, or tessellate each component square. Whichever the case it does edge fixing slightly before the tessellation. The renderer uses a "growFactor" when inserting a point into an edge, that varies the inserted points height (z) between the terrain blocks stored height and the hight of the inserted point on a line between the edge's ends. Hope that all makes sense.

Oh well that wraps up another ramble.
#13
11/07/2002 (7:47 pm)
All,

after much digging and hair pulling I finaly am able to display a "blended" heightmap in a gui control.

The key missing piece was "TerrainRender::mBlendBitmap", that is the blended bitmap used to generate the terrain [from what I can tell]

-Ron