Home

Ideal restriction of populations

Friday, March 07th, 2008 | Author:

A substantial problem with populations is their resource requirement. Having several populations in a scene often requires hundreds of megabytes or even gigabytes of memory.

Before Vista was released in 2007, the typical PC had been equipped with relatively little memory. But you don’t get very far with 512MB. Furthermore, the population process can take quite some time.
So, what do populations have to be like if you want them to be generated fast and economically?

Problems and Technical Background

The most important issue with populations on older machines is the high memory requirement. Sooner rather than later, TG2 will run out of memory. That can happen even when there’s still some free memory.

One technical solution are 64 bit machines running 64 bit operating systems. They can address/use over 4 billion times more memory than 32 bit systems. But even those have limitations. Using significantly more memory than is physically available causes the operating system to save data to the hard disk and only load them into memory when needed. Since accessing data on a hard disk drive typically takes about 100 times more time than reading data from memory, performance decreases dramatically. Finally, since there is no 64 bit version of TG2 yet, it is still limited to the limitations of the 32 bit system.

Effects

As an experiment, I filled a 10*10km area with a tree population with a distance between the individual instances of 2m. That’s a total of 25 million instances.
Having generated the population, TG2 crashes instantly. The task manager shows what happened. Before crashing, memory usage peaks at 2.3 GB. XP Home would have already given up long before this point, but even on an XP Pro machine with /3GB switch that lets the system use up to 3GB of memory, TG2 doesn’t find enough free memory because the program itself is still limited to 2GB.

Solutions
Object Spacing

One possible solution would be increasing the distance between the objects. 2m between two trees is too small. Doubling Object Spacing already reduces the instance count to a fourth!

But sometimes you may not want that. Think of a meadow. It should be completely covered with grass blades. Also, doubling Object Spacing might not be enough sometimes, as in our test scene.

A Mask for the Field of View

The area holding a population in TG2 is rectangular, which makes scaling and positioning easy, but it has disadvantages too. First, populations have a sharp border, which looks unrealistic.

Second, areas are being populated that aren’t even visible in the final image, which is a waste of memory and time. This may not be a real problem when rendering single images, but it will become one when rendering an animation where hundreds of square kilometers have to be populated with plants and stones, even though only a fraction of them is actually visible in a single frame – the rest is not adding anything but memory usage.

As a solution, one could limit the population using a mask. When rendering one particular single frame, we only need those trees that are within the field of view of the camera. Creating this mask is very easy. Simply project a white texture through the camera. In case the distribution of the population is already limited by an existing distribution or another shader, the network now looks like this:

The Image Map Shader loads our texture (a pure white 1x1px image). We have to use our Render Camera as the Projection Camera. The Merge Shader has Mix to A set to 1, Merge Color is checked and Color Merge Mode is set to Multiply (doesn’t matter which one). It is used as the mask for the Distribution Shader.

The result of that little trick is that the instances only appear where the camera is looking. This saves up to 50% of the instances!

Now, the bounding box serves no purpose anymore, other than limiting the radius within which the population exists. If we can make TG2 automatically generate a mask limiting the population to be confined within a certain radius, then we would be able to make the bounding box arbitrarily large without memory requirements skyrocketing. The box could cover the whole planet – our masks would still limit the population to the places where it’s actually needed!
However, it’s not advisable to do this, because generation time grows when you scale the box up and will probably be hours or days when covering a too large area.

Limit Population Radius for Animations

It’s clear that it only makes sense to use objects within a certain range. Outside that range, you won’t be able to perceive whether a group of pixels actually shows a tree or not. That’s the point where you can start replacing forests with a green surface layer.

This raises the question: What is that range? Where does rendering single objects start to become pointless? One could say, an object isn’t recognizable as such when it appears smaller than one pixel in the final image, because it is not identifiable if a single pixel shows a tree or a patch of grass. In some cases this assumption may not be true, in others even 2 or 3px can be unrecognizable. For this article, I assume one pixel as the threshold and call the corresponding distance from which an object appears smaller than one pixel “1px distance”.

bildwinkel-klein-en.pngThe field of view can be adjusted in the settings of the camera (Horizontal FoV). We assume that a tree has to be at least one pixel in height to deserve being rendered as an object. So, for a given resolution, the angular size of the object need not be greater than a certain value. Now, without further mathematics, here’s the result as a graph:

x-axis: Angle in Degree/°
y-axis: 1x Distance in km
(Plotted using MAFA function plotter)

These functions map a given field of view to the minimum distance from the camera at which an object with a width of 10m appears smaller than one pixel. The curves apply to resolutions of (from left to right) 800px, 1024px, 1280px, 1600px.
Example: At a resolution of 1024px and a FoV of 60° (the default) a tree is only as high as 1px from a distance of 10km onwards. When increasing the height of the object or the resolution by some factor, the 1px distance has to be increased by the same factor.
Example: If the object in the example above isn’t 10m, but rather 1m in height, the critical distance isn’t 10km but only 1km.

This diagram serves mainly as an orientation and theoretical base. Usually, you’ll only make a rough estimate of the needed distance. One more thing to keep in mind is that these figures apply to the horizontal field of view and to the width of an object. However, the height of many objects is by a multiple of their width. In that case, the 1px distance has to be multiplied by that factor too.

This is what an implementation in TG2 looks like:

The configuration of the distance shader can bee seen on the left. This shader paints a spot white, if its distance to the camera is smaller than the Near Distance, black if it’s greater than the Far Distance, values in between are blended.

We use a copy of the Merge Shader to multiply this new mask by the old one and use the result as Density Shader.

That’s it! TG2 now automatically decides where to put objects and where not to – independently from the population area. Nevertheless, this area should be made as small as possible to prevent the generation process from taking longer than necessary!

Critique and questions are welcome :)

Tags » «

Trackback: Trackback-URL | Comments Feed: RSS 2.0
Category: Advanced, How to.., Ideas, Meta, Pro, Tricks

You can leave a response.

12 Responses

  1. 1
    FrankB 

    Ingeniously simple and effective! Great idea, thanks.

  2. 2
    Matt Fairclough 

    This is a very useful article Nikita. However I’d like to suggest you should always use the smallest population bounding box possible, even when masking most of the population away, because the Populator needs to sample the density shader across the whole area before it knows how your population will be masked. The Populator considers candidate instances throughout the specified area and calls the density shader for each candidate to decide whether to create an instance. With more complex shaders being used to control the density or mask, larger population areas take longer to repopulate regardless of the number of final instances.

  3. 3
    Nikita 

    I know, I mentioned it at the end of the article. Ok, that’s really a bit late, especially after suggesting to put the whole planet into that box. :mrgreen: This article was written with memory consumption and flexibility in mind and I admit, if you really use this technique for animations, there is a huge trade-off between those goals (especially flexibility) and generation time. In production, one would probably try to move the population bounding box to wherever it’s needed instead of using such an academic approach to the problem.

    I removed a few mistakes now. I could probably rewrite some paragraphs too to make it perfect, but the target audience is experienced users who know what they’re doing, so I’ll just rely on their common sense.

    Thank you for your explanation/comment!

    PS: On a different note, I noticed that the method fails with edgewise renders. But it should work if you “Use vertical fov” when needed.

  4. 4
    Otakar 

    I think Planetside should seriously consider building this into Terragen as a simple checkbox (in the case of visible members of a population) and as a simple checkbox/text field combo (in the case of a cutoff based on distance). Otherwise I have to load a clip file every time I start a new project that includes populations, which is rather frequent and what’s more, I have to find the correct node and adjust the distance value.

  5. 5
    Nikita 

    For a fast implementation it’d probably be best to just use the edges of the area being rendered as the bounding box.. or bounding trapezoid.

    There’s still much room for optimization.. I guess we’ll see some improvement later this year. :)

  6. 6
    Volker 

    Very good ideas, and your techniques widen my understanding of TG2.

    One point are members of populations, whose origin is not visible, but that object’s top would normaly be visible (behind a hill).
    I think this could be an issue for animations – trees popping out of nowhere …

    Else, thanks for sharing!

  7. 7
    Nikita 

    Yes, but that shouldn’t happen with my technique – the mask covers the terrain completely, the back sides of mountains too.

  8. 8
    Ro-nin 

    Wow, You really helped me here!!!!
    You understand TG so much!!!!

  9. 9
    Fernando 

    Hi Nikita,
    Could you please tell me more about the Image Map Shader 01?
    I create the 1×1 px white image and loaded it inside the map shader. But I cannot get the same FOV between the B@W mask and the camera.
    To check it i’ve assigned the imagen map shader directly to the ground plane.
    My Render camera is at (0,0,0) with no rotations (0,0,0). The horizontal FOV (perspective) is 30º, but I get a white area of a quarter of my plane, the rest all black (viewing from top).
    Thanks in advance
    Fernando

  10. 10
    Nikita 

    Yes, the projected white area is always square. If you want it to exactly match the fov, you have to adjust the size values in the Image Map Shader.

    The best way to do this is to
    - activate Position center
    - enter 0.5, 0.5, 0 for position
    - 1 for the first size value and for the second value the inverse aspect ratio of your render. For example, if your render is 800×600 i.e. a 4:3 aspect ratio, you’d use the following values for size:
    1, 0.75, 0
    because 1/aspectRatio = 1/(4/3) = 3/4 = 0.75

    This way, you’ll get a mask that exactly matches your mask.

    Does this answer your question?

  11. 11
    Fernando 

    Thank you Nikita,
    Solved so far,
    Which is the better way (real time?) to check the mask before applying it to the population?
    The one I’m using is to apply it to the ground plane…

  12. 12
    Nikita 

    I think the easiest way would be to check Preview instances in the population and press Populate now.

Leave a Reply