How does WoW's terrain generation work?

Started by
17 comments, last by Beather 14 years, 3 months ago
I think I'm starting to get it! Thanks a lot for your help!
Advertisement
You also don't necessarily HAVE to store a mapID, your X/Y coordinates could represent a global value, although depending on the world you may need more precision. I'd likely use the mapID myself, although I'd use an X/Y value for that too, assuming I split up the terrain based on size and not complexity.

Also consider that this is a great place to implement LOD for your terrain. Anntor told you to imagine circles around your player which decide when you load/unload elements of the map. Also consider that you could have a VERY large circle for when to load a very low-detail terrain with a low-res texture, and another for when you want to load higher level data.
-----------------------------------------------“The best, most affordable way to save the most lives and improve overall health is to increase the number of trained local, primary healthcare workers.”Learn how you can help at www.ghets.org
I have made exact copy of WoW like terrain renderer that can also display WoW terrain in past. Basically you have chunks of 9x9 vertices arranged in vertex buffer like VertexBuffer contains (chunk1Vertices, chunk2Vertices, etc) and have variations of index buffers for each type of transition so you can display lower triangle counts using same vertex buffers. Note the X pattern.



Each chunk have maximum of 4 textures assigned for it - they are using for splat mapping (multitexturing).

Also i can suggest that you store all the information you need in just one single Vector3 Position like this:

CPU - main memory will have information for each chunk like:
4 textures (ID, hash or similar lookup)
Vector3 base position - this will be position where X and Z coordinates (left handed system) are your chunk top left corner in game world and Y coordinate is average vertex Y position in chunk

GPU - video memory will have information like this:
VertexBuffer - only one Position (Vector3) is needed - you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates, ranging from 0 to 1 stored in Y and Z components of your position so in vertex shader you can construct your position like this:

       float3 outPos;       Pos.Y = shaderInputBasePos.Y + inPos.X; //relative height stored in X of Position       Pos.X = shaderInputBasePos.X + inPos.Y * CHUNK_SCALE; //Texcoord component used to offset vertex position X       Pos.Z = shaderInputBasePos.Z + inPos.Z * CHUNK_SCALE; //Texcoord component used to offset vertex position Z       float2 outTexcoodrd;       outTexcoodrd.X = inPos.Y * TEXTURE_SCALE;       outTexcoodrd.Y = inPos.Z * TEXTURE_SCALE;


For loading/unloading you group say 8x8 chunks together and load/unload them when you are in range. DONT create or destroy vertex buffers in runtime, you will have say, 2x2 or 3x3 groups in sight range at once so you will only need to replace old data in vertex buffer (only X value of your position if you use technique i told) with new one when old disappears and new appears. Also you replace your old splat texture with new one for texturing so the rendering of stuff comes together like this:

     foreach visible chunkGroup           set splat texture to chunkGroup splat texture                    foreach visible chunk in current chunkGroup              set basePosition for vertex shader              set 4 diffuse textures for pixel shader              RenderCHUNK(): vertexBuffer = chunkGroup vertexBuffer,                             indexBuffer = calculated LOD for current chunk,                             vertexOffset = current chunk vertex offset,                             vertex & tris count = chunk vertex & tris counts (constant)
Thank you very much for your replies.

Quote:Original post by ItsDan
You also don't necessarily HAVE to store a mapID, your X/Y coordinates could represent a global value, although depending on the world you may need more precision. I'd likely use the mapID myself, although I'd use an X/Y value for that too, assuming I split up the terrain based on size and not complexity.


I think I understand. Do you think that using a mapID gives trouble when transitioning from one mapID to the other? It seems easier to use a global X and Y coordinate.


Quote:Original post by Semei
Basically you have chunks of 9x9 vertices arranged in vertex buffer like VertexBuffer contains (chunk1Vertices, chunk2Vertices, etc) and have variations of index buffers for each type of transition so you can display lower triangle counts using same vertex buffers. Note the X pattern.


What is a "chunk" exactly? Is it a 3D mesh in memory?

Why 9x9 ?
Would it possible for you to show me a piece of your source code in DirectX C++, and/or the shader you used? (I understand if not)

Also, what's with the X pattern? I don't see the point.

Quote:Original post by Semei
Each chunk have maximum of 4 textures assigned for it - they are using for splat mapping (multitexturing).


Why 4 textures? Do you mean to assign a control texture: one with red, green and blue in it to determine which of three textures to draw (e.g. rock, sand, grass) ?


Quote:Original post by Semei
GPU - video memory will have information like this:
VertexBuffer - only one Position (Vector3) is needed - you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates, ranging from 0 to 1 stored in Y and Z components of your position so in vertex shader you can construct your position like this:


float3 outPos;
Pos.Y = shaderInputBasePos.Y + inPos.X; //relative height stored in X of Position
Pos.X = shaderInputBasePos.X + inPos.Y * CHUNK_SCALE; //Texcoord component used to offset vertex position X
Pos.Z = shaderInputBasePos.Z + inPos.Z * CHUNK_SCALE; //Texcoord component used to offset vertex position Z

float2 outTexcoodrd;
outTexcoodrd.X = inPos.Y * TEXTURE_SCALE;
outTexcoodrd.Y = inPos.Z * TEXTURE_SCALE;


you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates

Why vertex Y into X component? What has this to do with texture coordinates?
(I'm assuming that Y-axis is up)

Is shaderInputBasePos the top left corner of the mesh (chunk)?
Is inPos the actual vertex position being processed?

Why do you add the vertex X value to the shaderInputBasePos Y position?

What is CHUNK_SCALE and TEXTURE_SCALE used for more specifically?



Quote:Original post by Semei
For loading/unloading you group say 8x8 chunks together and load/unload them when you are in range. DONT create or destroy vertex buffers in runtime, you will have say, 2x2 or 3x3 groups in sight range at once so you will only need to replace old data in vertex buffer (only X value of your position if you use technique i told) with new one when old disappears and new appears. Also you replace your old splat texture with new one for texturing so the rendering of stuff comes together like this:


foreach visible chunkGroup
set splat texture to chunkGroup splat texture

foreach visible chunk in current chunkGroup
set basePosition for vertex shader
set 4 diffuse textures for pixel shader

RenderCHUNK(): vertexBuffer = chunkGroup vertexBuffer,
indexBuffer = calculated LOD for current chunk,
vertexOffset = current chunk vertex offset,
vertex & tris count = chunk vertex & tris counts (constant)


I'm sorry, I read it a few times but I still don't understand very well.
I also have to admit I am inexperienced with vertex and index buffers, so bear with me.

Thank you very much!
Don't get too hung up on the details yet. Some of the comments are more related to how WoW specifically handles things (or appears to anyways) when your question seems to be more along the lines of "how do I create a seamless, large-scale world without loading screens".

The vertex buffer comment relates to the fact that creating/destroying memory on the GPU is "expensive" and so you should reuse the existing buffers with new data and not create a new buffer when you load a new chunk, since you're likely replacing an old chunk. But it all boils down to implementation details.

Practice with whatever you're comfortable with first. Then refactor, optimize, rewrite, whatever needs to be done.
-----------------------------------------------“The best, most affordable way to save the most lives and improve overall health is to increase the number of trained local, primary healthcare workers.”Learn how you can help at www.ghets.org
*What is a "chunk" exactly? Is it a 3D mesh in memory?

Chunk is one piece (9x9) vertices, as it could be chunk of meat this is chunk of vertices so simple as it is there is no hidden meaning.

*Why 9x9 ?

Because its 256 triangles. Cache, blah blah, then its power of 2 lower tiles - usfefull for lod. Gives you 8x8 4 triangle tiles and hey we all like 8 and this allows you to create 4 lod levels starting from 8x8 to 4x4, 2x2 and 1x1

*Also, what's with the X pattern? I don't see the point.

LOD. Try to create some basic lod transitions and see how it comes together also note that this way you wont have one way repeating pattern as you would if you used regular approach - this way each triangle is more square, less rectangle so it looks better. You can create LOD transitions without using X pattern, you dont HAVE to use it. Im talking about WoW now. If you look at the picture and try to find lower detail representations of X pattern using the same vertices as higher lod you will be surprised how nice it all falls together ;)

*Why 4 textures? Do you mean to assign a control texture: one with red, green and blue in it to determine which of three textures to draw (e.g. rock, sand, grass) ?

I do, 4 because there is 4 texture channels in one ARGB texture and having 8 is a bit harsh on gpu.

*Why vertex Y into X component? What has this to do with texture coordinates?
(I'm assuming that Y-axis is up)

Information stored in vertex buffer for each vertex:
1) Y position of vertex (as like height field)
2) Texture coodrinates ,ranging from 0 to 1 with 0,0 being top left corner of tile and 1,1 bot right.

Layout of data:
1) Use only one POSITION
2) Store Y position (you can call it Height if you are confused with that "Y") in X component
3) Store texcoords in remaining (Y & Z) channels.

Why? Because its simple. First height, then 2x texcoord.

*Is shaderInputBasePos the top left corner of the mesh (chunk)?

It is.

*Is inPos the actual vertex position being processed?

"in"Pos - this is data that you have in vertex buffer.

*Why do you add the vertex X value to the shaderInputBasePos Y position?

Because there is Y Position - Height stored in X component of vertex buffers positions.

*What is CHUNK_SCALE and TEXTURE_SCALE used for more specifically?

CHUNK_SCALE will determine how big are dimensions of one chunk. for example, 8.

TEXTURE_SCALE will determine how many times texture will repeat thro one chunk, for example, 4.

Aint that obvious? :D

*I'm sorry, I read it a few times but I still don't understand very well.
I also have to admit I am inexperienced with vertex and index buffers, so bear with me.

You must understand vertex and index buffers in and out in middle of night to implement this, but hey, its not hard to learn it.

Note that this would be EXCELLENT lesson for you, i highly suggest you to make it as it contains tons and tons of useful learning stuff in it!
Quote:Original post by ItsDan
Don't get too hung up on the details yet...

I second that [grin]

Quote:Original post by Beather
...What is a "chunk" exactly? Is it a 3D mesh in memory?...

'Chunk' is just a block of data (Wikipedia: block, Google: memory chunks). What data is in is up to you and your application requirements. Chunks are fixed-size refering to the mapping of their carried data.

For example, person A writes a 3D terrain renderer and defines a terrain chunk as a mesh dataset (part of the whole map mesh) plus 2 textures. Person B writes a 2D isometric map renderer and thinks of a chunk as a set of 10 tile images, a list of objects inside that part of the map and some statistical data.
Both talk to each other and saying: "I load my adjacent chunks, when i'm getting close to that part of the map." And both are right, despite they have different chunk definitions. [grin]
Little nitpick: The thread title reads "terrain generation", the topic is "terrain rendering". Those are overlapping, yet different problems.
Thank you for all your help!

I am planning to make this project soon, so this was very helpful!

I am going to study first more about vertex and index buffers, so that I know better how things work internally.

Nick

This topic is closed to new replies.

Advertisement