PDA

View Full Version : Rendering Large Terrain



MartinB
06-02-2014, 08:57 AM
Hello all!

I am trying to render a large terrain in OpenGL. My terrain consists of multiple terrain blocks of size 1000x1000 meters (1001x1001 vertices).
Each block consist of multiple driangle strips drawn by means of the glMultiDrawElements(GL_TRIANGLE_STRIP, ...) function.
But when I for example create a terrain of size 6x6 kilometers (6x6 blocks) I get a poor performance (only about 2fps).

How could I improve It? Can I use triangle strips or should I use another primitive type if I want to use a LOD technique?
Is there a simple terrain LOD tutorial for beginers with a simple code samples which explains it step by step?
All tutorials I have found were too much theoretical or were very complex and complicated for learning.

Could you please help me?

Thanks so much :)

-Martin

rodrigoloc
06-02-2014, 10:11 AM
What OpenGL version and graphic card are you using?
Could you show us how are you drawing that meshes?

Edit: Triangle strips are (usually) one of the fastest modes so there's no problem with that.

MartinB
06-03-2014, 04:11 AM
I am using Nvidia GeForce gt555m (notebook) with OpenGL 4x, but I like to preserve compatibility with OpenGL 3x.
The terrain meshes are drawn by means of the glMultiDrawElements function, i. e. terrain of the size for example 1x1 km consists of 1000 triangle strips of width 1m and length of 1 km.
The vertices are stored in an float array of size 1000x1000x3. The first triangle strip has indices 0, 1000, 1, 1001... The second strip has indices 1000, 2000, 1001, 2001... etc...

I would like to use a LOD technique because I think that drawing a large terrain without LOD is too clumsy. But I don't know where to start...

Brokenmind
06-03-2014, 04:54 AM
Save your height vertices in a height map
Save a terrain block (chunk) as a VBO (preferrably much smaller than yours, 32x32 has proven to be good)
Write a drawing function: Define what to draw and how detailed to draw it based on the distance to the camera
Draw it instanced (all the necessary information for each chunk can be stored in 4 floats)

An old image of mine displaying the precision of the chunks can be found here (http://s14.directupload.net/images/user/131101/qbgnqrid.png).

MartinB
06-03-2014, 12:53 PM
Okay, so I should learn instanced rendering first. I have never did it...

Aleksandar
06-03-2014, 03:33 PM
Not necessarily. Most terrain rendering algorithms don't use instancing. I prefer clipmaps.
Take a look at vterrain (http://vterrain.org/).

thokra
06-04-2014, 03:11 AM
If you're able to leverage tessellation, this (http://docs.nvidia.com/gameworks/content/gameworkslibrary/graphicssamples/opengl_samples/terraintessellationsample.htm) approach is also noteworthy. Or this (http://codeflow.org/entries/2010/nov/07/opengl-4-tessellation/).

Aleksandar
06-04-2014, 04:03 AM
Tessellation shader approach is not superior to vertex shader approach because of at least two reasons: VS has wider support and there is no limitation on the size of the blocks.

thokra
06-04-2014, 04:13 AM
Aleksandar: I didn't say it was superior or the only way - I just proposed it as another contemporary alternative. It's definitely better than ROAMing. ;)

Aleksandar
06-04-2014, 04:38 AM
Sorry, I didn't want to offend you.
Definitely better! ROAM is a prehistoric algorithm.

thokra
06-04-2014, 04:42 AM
Sorry, I didn't want to offend you.

None taken! Afterall, this isn't StackOverflow. ;)


ROAM is a prehistoric algorithm.

Yeah, implemented that years ago, pretty neat in theory though. I wonder how far one can push it with current GL features ...

I mean even with tessallation it's basically a progressive mesh approach - not too different from ROAM. The biggest issue with classical ROAM is that except for the final rendering, it's purely CPU based.

Aleksandar
06-04-2014, 05:00 AM
ROAM appeared in a time when GPUs could render very few triangles. So, it was important to decrease their number. ROAM is very CPU intensive.
On the other hand, nowadays GPUs can render immense number of triangles, especially if the "pattern" is good. So, regular grid approach is the best choice in the last decade.
Now it is more important to efficiently feed GPU than to optimize the number of triangles. That's why I prefer clipmaps. I have finished "my version" of clipmaps and I hope I'll make it public soon.
Ups, I didn't mean to advertise! ;)

thokra
06-04-2014, 05:02 AM
I have finished "my version" of clipmaps and I hope I'll make it public soon.!

Giev! Need! ;) Looking forward to it!

MartinB
06-04-2014, 05:26 AM
Thank you folks :)

So I will now focus on geoclipmaping.
I have found this site: http://malideveloper.arm.com/develop-for-mali/sample-code/terrain-rendering-with-geometry-clipmaps/.
May it be?

To Alexandar: ...I am also looking forward to your version of clipmaps :)

Aleksandar
06-04-2014, 05:42 AM
If some one follow my questions on the forum maybe could assume what I'm working on. Just to make a short announcement (sorry for hijacking the thread) I have succeeded to make clipmaps applicable to real ellipsoid (in my case WGS84, since I've applied it to Earth visualization). Ellipsoid is subpixel exact using just single precision on the GPU for all distances, from several millions of km to several micrometers from the surface. Another benefit is 1:1 vertex to source data mapping. Something that Spherical clipmaps could't have (that's the main reason that algorithm didn't succeed to be popular).

Aleksandar
06-04-2014, 06:35 AM
May it be?

I'll also suggest reading an original paper (http://research.microsoft.com/en-us/um/people/hoppe/proj/gpugcm/) (or to be more precise, a book chapter). And for the start, I'll recommend to make levels of just a single block. Also, don't bother with compression/decompression at the start. Use shorts for hight values (a single channel 16-bit texture) and DXT1 for texture-overlay.

MartinB
06-04-2014, 02:01 PM
Yes, thank you for that paper, I am studying it right now. It looks that it is written for DirectX but I think it will help me anyway :)

thokra
06-05-2014, 02:38 AM
It looks that it is written for DirectX but I think it will help me anyway

This cannot be surprising, Hugues Hoppe being a principal researcher at Microsoft Reasearch. ;)

However, from my experience, mapping the algorithm to OpenGL is straightforward.

MartinB
06-25-2014, 04:06 AM
Yes, I have found that some things are similar to DirectX :) ...That paper helped me very much, but there is just one thing I don't understand: do I need just one height texture for whole terrain? Won't it be too big?
...And in the paper there is written : "During the update step, we modify regions of these textures by rendering to them using a pixel shader." Is it possible to render to texture using a GLSL shader? I have never heard about it... Thanks for help

thokra
06-25-2014, 04:29 AM
Is it possible to render to texture using a GLSL shader?

At least two, the usual render-to-texture approach using framebuffer objects and image load/store - however, the most recent GPU clipmaps paper is from 2005 and the latter was only introduced in GL 4.2 and I think D3D11 (or 10.1). I'd have to read through the paper again because I really don't remember that part.


do I need just one height texture for whole terrain? Won't it be too big?

I can't think of any other approach than the obvious two: either you have the whole terrain in memory or you only have chunks and stream the chunks you need (also, with sparse textures this can get very efficient).

IIRC, the clipmaps paper proposes a decompression stage on the GPU (and a compression stage on the CPU) so they can get their complete height-map down to 400 something MBs in VRAM.

That said, you don't need compression/decompression at all for implementing the rest of the paper.

MartinB
06-25-2014, 08:39 AM
Okay, so is it possible to use only one very big compressed heightmap texture (i.e. bigger than 4096x4096 pixels) for whole landscape?

Aleksandar
06-25-2014, 03:24 PM
I'm sorry I'm engaged at work for more than 12 hours these days and have no time to give a complete answer but I'll try to help in a few sentences.


...That paper helped me very much, but there is just one thing I don't understand: do I need just one height texture for whole terrain? Won't it be too big?
This proves you actually don't understand clipmaps.
For a flat terrain, you need a single texture array for storing heights. The paper is rather old. In those days texture arrays didn't exist, hence they probably used several textures, a single texture for each level.

Unlike full mipmaps that have exponential growth with each new level, clipmaps have linear growth. They enable caching relevant parts of mipmap levels resulting in consistent display of a texture occupying several pentabytes by using just several hundreds of megabytes.


...And in the paper there is written : "During the update step, we modify regions of these textures by rendering to them using a pixel shader." Is it possible to render to texture using a GLSL shader? I have never heard about it...
"Render to texture" is a quite common technique. It is strange you haven't heard about it. It is not necessary to use "render to texture" with clipmaps. For example, I'm using glTexSubImage3D() for a level update. The rendering to a level enables filling a level with the data of the coarser one and have a consistent draw without waiting for the proper resolution data or disabling level not properly updated yet. All in all, desirable but not mandatory feature for a clipmap update.


I can't think of any other approach than the obvious two: either you have the whole terrain in memory or you only have chunks and stream the chunks you need (also, with sparse textures this can get very efficient).
For God's sake, NO!
Clipmaps are a diametrically opposite approach to chunked LOD. The whole terrain, as I've already said, can occupy several pentabytes. That cannot be stored even on a hard disk. The point of a clipmap is to cache just portions of the levels of the full mipmap that are relevant for the rendering. The levels are updated toroidally.


IIRC, the clipmaps paper proposes a decompression stage on the GPU (and a compression stage on the CPU) so they can get their complete height-map down to 400 something MBs in VRAM.
They demonstrated a very efficient compression/decompression scheme. My respect! But it is not enough for all purposes. I suggest an out-of-core approach where required data are read from the disk or even network. Also, I suggest using the same approach for the heights as for the texture-overlay.


Okay, so is it possible to use only one very big compressed heightmap texture (i.e. bigger than 4096x4096 pixels) for whole landscape?
Not recommended. A single texture array yes, but a single texture certainly not!
512x512 pix height-map levels are pretty acceptable for a full HD displays. The texture overlay should have at least 2048x2048 pix size. 4Kx4K is OK for 4K displays, but the update cost is usually high for the highest possible sharpness of the finest level. Generally, the size of the level should be greater or equal to the screen size in order to have valid display.

MartinB
06-26-2014, 12:23 PM
Never mind, I am also not frequently on the internet.



"Render to texture" is a quite common technique. It is strange you haven't heard about it.

Yes of course, I know what it basically means "rendering to a texture". But I have never heard how to do it in the fragment shader :).

But I think I finally understand the basic idea of clipmaps.
So, I need a single 3D texture or a single 2D texture array where for example the top layer represents ONLY the small square around the viewer in high resolution and the bottom layer represents the coarsest level and In such a way the farthest marginal ring in lowest resolution and therefore the center of the lower layers is never used. And as the viewer moves I should update the heightmap textures by runtime loading data (for example) from a file. Do I understand it correctly?

Aleksandar
06-26-2014, 04:11 PM
Basically, yes. You've got it. :)
But never use 3D texture, just 2D texture array. Also, never update from the hard disk. Use a memory cache instead (make a clipmap also in RAM, update it from the files on a disk and use it to update GPU clipmaps). Update of a GPU clipmap is usually very fine and extremely fast. Sometimes only a 4pix wide strip. While update of the clipmap's cache stored in the RAM is very coarse and slow (updated using tiles stored in files).

MartinB
06-27-2014, 07:00 AM
I am also glad I have quite got it :)



Use a memory cache instead (make a clipmap also in RAM, update it from the files on a disk and use it to update GPU clipmaps).


Could you explain it more in detail? I am sorry, I am not a professional of your format :D .

Aleksandar
06-29-2014, 03:58 AM
Well, let's say we have a clipmap with the level size of 2048x2048 texels. If we want to go east, we need to update some levels. Also, let's we have texture tiles on the disk of size 1024x1024 tex. The smallest update of a level is 4x2048 tex. Direct update from the files require 3 tiles read (3x1024x1024), and extracting proper 4 texel wide strip from them. OS certainly caches disk reads, and the files are probably in the memory, if we have already accessed them in the near past. But, that cache is not spatially organized, because Windows knows nothing about our algorithm. Also, there is an additional task to extract required data and repacking.

We would save a lot of time if we create a clipmap in the main memory that contains the certain number of tiles in each level. Instead of fine update, like GPU clipmap requires, RAM cache is updated by the whole tile row or column. It is much coarser, but suitable for disk access.

MartinB
07-17-2014, 02:12 PM
I am sorry, I haven't been here for a long time, I was working on the buffers for rendering the levels and I also had another work so I was busy.

Allright, so I should have tiles where I store the heights and from those tiles I should update the final height texture array used for the clipmap and I should copy the pixels /for example/ using glGetTexImage and glTexSubimage3D?

Now I only must invent algorythms for proper updating :D

Aleksandar
07-18-2014, 04:13 AM
Why do you need glGetTexImage()?

MartinB
07-18-2014, 06:31 AM
Why do you need glGetTexImage()?

I thought that I need it to copy pixels from the tile texture to the final height texture used in the clipmap when updating the clipmap. Is there another / simpler / way to do this? I would welcome it.

Or how can I update the clipmap when I am moving across the terrain? Could you please put it simply so I can understand it better?

Aleksandar
07-18-2014, 07:24 AM
I thought that I need it to copy pixels from the tile texture to the final height texture used in the clipmap when updating the clipmap.
When I talked about the mem cache, I didn't mean graphics card memory. The cache is in the main memory. So, use regular way to cut proper rectangle from it and send to the texture.
You won't have enough memory to keep the cache in the graphics memory. Or, maybe I'm wrong... :)
On the other hand, there would be unnecessarily huge transfer to the GPU when new tiles are read from the disk. Dozens of MB instead of few kB required.


Or how can I update the clipmap when I am moving across the terrain? Could you please put it simply so I can understand it better?
Use toroidal update as described in all clipmaps papers. If the viewer's distance from the center of the current level is greater than some threshold (defined by the border size - the amount of texture reserved for the update), you should send request for the update. The threshold should be checked in both directions (send a separate request for each direction). As an answer to that request, the data provider should check the cache, cut proper strips and send then to the texture level that needs update. In the request/response you should code the direction of update so the level knows which part of the texture should be updated when the response comes.

MartinB
07-19-2014, 11:07 AM
The cache is in the main memory. So, use regular way to cut proper rectangle from it and send to the texture.
You won't have enough memory to keep the cache in the graphics memory.

So can I store the heights data for example as an array?


Use toroidal update as described in all clipmaps papers.

Ou yeah, I think I finally understand it :D It looks that as I moves along the height texture for example to the right I must add new heights data (resp. overwrite the old data) on the left border of the texture and as I move on I am simply adding data to the right side of previously added data(resp. overwrite the old data)... And when I reach the right border of the texture I automatically jump to the left border. Do I understand it correctly?