This blog series discusses some ideas and issues around rendering foliage. Why? I’m on Intel’s Game Technology Development team. We make samples around gaming on Intel platforms. Hardware and APIs constantly evolve. We ship small example programs (with source) showing how game developers might use the new features. This often takes the form of: “The new features allow game developers to improve existing technology”. Foliage often comes up in these discussions. It’s a common feature with interesting and unique technical challenges.
This first version is a baseline; it doesn’t focus on specific HW or API features. The intent is for future samples to show how it can be improved with new HW and/or API features. Note also that, while it doesn’t exactly use “programmer art”, it does use “prototype art”. We use only two tree models, and two grass models. A real game would use many more of each. A production-ready system would also have a more-complete feature set. We’ll discuss some of them later in the series.
The project is available here: http://github.com/DougMcNabbIntel/Foliage
Figure 1 Foliage screenshot
What’s So Special About Rendering Foliage?
It’s simple enough to use brute-force and issue separate draw calls for each object. It's simple, but not efficient. The cost of independently rendering every object would quickly add up, limiting our scene to a few thousand visible objects per-frame. Filling the world with unique objects would also limit the total number of objects we could have; memory is limited. Some thought and work enables us to fill our world with objects.
There are many possible foliage scenarios. We focus on the challenge of covering a relatively-large world; e.g., an “open world” game where the player isn’t “on rails”. Other approaches could easily be more appropriate if the camera is sufficiently constrained.
Our first specialization is to split the work in two:
- 3D objects near the camera
- 2D billboard proxies for distant objects, and for small objects near the camera
Both have interesting characteristics, and present different challenges. Combining them to form a seamless experience adds more challenges – increasing the “degree of difficulty”. Both of them need to support the following:
- Transparency Sorting
We render objects that are close to the camera as more-or-less "regular" 3D objects. A brute-force approach would have the artist fill the world with unique objects. This would, of course, be too expensive; it would require more memory and time than we have.
Instancing increases efficiency. We use simple instancing: We render each object multiple times; we store each object only once, but draw it multiple times. We don’t yet use the API’s instancing support to draw multiple instances with a single call. We leave that for a future sample - to illustrate the relative value of that approach.
We’ll explore the details in upcoming blog posts.
2D Billboard Proxies
We render distant objects as two-triangle ("quad"), camera-facing billboards. We collect them into "patches". Each patch contains thousands of objects, covering a rectangular area on the ground. As expected, this is much faster than separately rendering the individual objects, with thousands of tiny triangles each. It isn’t all roses though. These billboard proxies are different from their corresponding 3D objects. We do what we can to have them both produce the same visual result. But, the results are different. Minimizing artifacts when transitioning between 3D objects and 2D billboards is a significant challenge.
We’ll cover more details in upcoming blog posts. Stay tuned for more details!
Update: Part 2 is now online: http://software.intel.com/en-us/blogs/2013/05/19/foliage-patch-organizing-our-data-0