Forest Rendering

Greetings again all. :slight_smile:

First of all, my apologies for my absence of late - I recently moved, don’t yet have an internet connection at home; I’m writing this from work. That said, on with the purpose of this post!

When I originally came here, as I recall, I did so asking after the engine’s suitability to a project that I had in mind one which involved a forest setting. After quite some time (the project was conceived well before my arrival here, as I recall), I have begun research in earnest on the problem of rendering such a forest as I have in mind.

This brings me to one request, and one question.

First, does anyone have any advice regarding forest rendering, either in general or in using Panda3D specifically? Recommendations of third party APIs or tools would be most welcome (although I alas have come to the conclusion that SpeedTree is rather well out of a wise price range for me :confused: ), as well as advice on building my own (which is what, having done some searching, I believe I will likely end up doing). I have already done some research, and have some ideas, but I would be very glad of additional input, I believe. :slight_smile:

This brings me to a specific point, and my question. I have been advised that modelling the leaves of nearby trees as textured quads might not be overly taxing on systems (as I had previously imagined) if I employ geometry instancing, essentially having one leaf that is placed and rendered over and over again.

I’m not referring here to scene-graph instancing, which is, if I’m not much mistaken, the form described in the manual. I’m told that geometry instancing is usually handled in a shader; am I correct in thinking, then, that there should be little trouble in implementing it under Panda?

My thanks for any help given. :slight_smile:

[edit] A little detail about what I have in mind, to give an impression, I hope, of where I stand at the moment in my plans:

I realise that a primary element of achieving this is level-of-detail. What I have in mind at the moment is essentially high-detail trees at close range, with separate leaves, low-detail trees at middle distance, with leaf clump quads, lower-detail-yet trees at far distance, with few, larger clump quads, and at far distance billboarded sprites, each showing one of a fixed set of pre-rendered images based on angle to the viewer.

Since I currently intend on some degree of stylisation of trees, increasing with distance, I hope that fewer sprites and leaf-clump images may be used than might otherwise look good.

hypnos once wrote a small jungle-engine demo which featured randomly created forest.
vterrain provides some good papers about plant rendering. in short:
make groups of plants. like grass, small bushes, high bushes, small trees, high trees.
each group with their own lod settings. large objects usualy are visible at greater distance than smaller ones.
then you need to arrange the vegetation into render-friendly batches. means. flatten a bunch of nearby trees into a single node.
gotta hurry for now. i’ll be back later on.

Thank you. :slight_smile:

I in fact remembered and downloaded the jungle engine today, and am hoping to give it a shot tonight. I think that I tried it once before, but had problems of some sort, but hopefully I’ll either not have any or will solve them this time.

As to VTerrain, I think that I may have looked there - is this the place?

As to groups of plants, hmmm… Would that not produce too little variation in the world? (I should perhaps have mentioned that this is intended to be in the first person perspective, with the environment being rather significant, I think, in setting the atmosphere - I would very much like to have a high degree of variation between trees, or at least in the placement of the trees, at least at close range. I’m currently thinking about using procedural generation to increase the variety of trees displayed.)

However, I might give flattening a brief shot tonight - I put together a quick test a little while ago, using a simple, untextured tree model (of somewhere between 7000 and 8000 vertices, I think, which amounts to rather few leaves, I feel), on the GeoMipTerrain that I had been testing with previously, and found that my framerate dropped to around 30fps (with V-Synch on, admittedly; I don’t think that I had at the time that part of the manual that mentioned the appropriate setting for that) with enough of them on-screen at once.

groups can be created on the fly. just create a copy of your model, arrange them the way you like, and flatten them.
its just that graficcards prefer fewer, bigger bunches over many small ones. this can really break performance if you dont have an eye on it.

btw. the articles you linked seems to be the one i remembered.
they also somewhere explaing the layering with the plants.

if you are looking for a high degree of realism. this one might be of intrest. quite nice mechanism. mainly for grass and the likes. less suited for trees, those prefer traditional LOD.
http://www.kevinboulanger.net/grass.html
one word about LOD. using normal maps on textured quads( may it be for leaves, bunch of leaves or entire trees) greatly improves the look since lighting can be calculated correctly.

I don’t know if this will be of any use but there is an open source attempt to implement something like speed tree, but I don’t know how mature/usable it is - haven’t had a close look, but the impression I get is its a bit dead/limited. Screenshots are pretty though!
Anyway, have a poke around opentreelib.sourceforge.net/ - might save you some time or give you some ideas if nothing else.

Well, I took a look at the jungle engine. I don’t believe that it will turn out to be of great use in this case, based on my test run, but I might yet find some use within the code.

Hmm… But what is the cost of creating these groups on the fly, as I travel through the forest? I’m hoping to create a pretty big forest, so I would expect to be dropping and re-creating trees as I move out of and into visual range of them…

Come to that, might it not be better to generate most of the geometry (or at least interpret it) on the GPU? For one thing, I think that it would probably be wise to handle the leaves via geometry instancing, rather than including them as part of a model passed to the GPU…

As to what I actually pass, might I not get away with passing a representation of a tree (something similar to the “stick-trees” in your tree generator), and then put together the appropriate geometry on the GPU end? That way I would be passing only something fairly lightweight to the GPU.

“Layering”? In what sense? Do you mean render order?

Thank you for the link - I’ll probably read it later tonight - and for the advice about normal maps - I’ll try to remember that once I get to that stage. :slight_smile:

Thank you very much for that, lethe - it does indeed look pretty good. :slight_smile:

Unfortunately, as you say, it does look rather dead, the downloads section lists only a nightly tarball and a link to their CVS repository - no releases at all, and there is only an IRC channel mentioned under “Contact”. :confused:

about optimizing geometry into groups and flatten them. if you dont do this, your framerate will be somewhere close to zero cause the graficcard is TERRIBLE inefficient when rendering many many individual geoms.
you could try gpu based instancing or even geometry shader. but i’m pretty sure that panda does not yet support later ones. dunno about instancing gpu based.

layering the vegetation is also a very important thing.
layering as in one “group plants by on-screen size”
so all grass, flowers, and other ground-near plants go in one layer. you only make this layer visible on a small spot around the camera.
one layer for smaller bushes, and the like. which is visible for a longer range than grass but still small.
one layer for huge bushes, one for small tree’s one for big trees,
the further the distance to the camera the more you can hide.

btw. you’ll notice that you dont need that many different models. especially smaller vegetation can be re-used without problems. 4 or 5 different modelsl for a bush or a simple tree are more than enough if you add some random scale and rotation.

what also might be of high intrest for you. a vertex shader which animates your vegetation. its totaly impossible to do that on the cpu. but if you use vertex-colors to controll the ammount of movement and maybe direction. then you can get a quite neat dynamic into your forest without sacrificing performance.

Hum… I’m not convinced that having only a few tree models per type for the nearest level of detail will look sufficiently good for this game. I’m hoping for the environment to be something worth looking at in itself; this isn’t an action game, so the player should have time in most cases.

For smaller, more nondescript things - bushes, ground flowers, grass, etc., perhaps, yes, though.

I see what you mean about flattening - although your mention of Panda not supporting some such things is a little worrying…

As to layers, I believe that I see what you mean, now - having different level of detail distances and settings for different object types. That is indeed a good idea, I think, although one that I’ll probably put off worrying about once I have the trees rendering to my satisfaction.

The vertex shader movement idea is a good one, I think - thank you. :slight_smile:

Panda does not currently support geometry shaders or on-card instancing. Both of these are relatively new ideas that are currently riding high on the hype curve. Geometry shaders sound great, but they don’t actually solve the problem at hand, unless your only limiting factor is bandwidth between the card and the CPU. They also cause their own sets of problems; and they’re only supported by a very few graphics cards right now anyway.

Panda will no doubt support these concepts eventually, when they have matured a bit more.

David

I agree, I was excited by geometry shades but they are pretty useless.
I recommend looking more at how crysis did its jungle. There is lots of research floating around the web.

Having self shadowing billboards is very important in this case to represent leaf masses at distance. Billboards should have the color layer and the depth layer and be showed correclty.

Chapter 4: Next-Generation SpeedTree Rendering of developer.nvidia.com/object/gpu-gems-3.html is very useful even if you are not using speedtree.

Hum… That’s somewhat disquieting. Thank you for bringing this to my attention. :confused:

What do you recommend as a means to handling leaves at close range, then?

Well, I had hoped (and still hope) to have procedurally-generated trees, to vary tree appearance, leaving me with a fair few meshes being sent to the GPU as one moves around, each with at least one lower-level-of-detail version for the mid-distance, I would think.

To this end, I had thought of either creating a minimal representation on the CPU side and sending that off to the GPU, where a proper mesh would be generated, or generating a single mesh representation per tree and having that interpreted, perhaps by skipping vertices or groups of vertices to create lower-poly meshes, for example.

Of course, I doubt that it would by any means be my only bottleneck by any means…

Thank you - that sounds like a good idea. :slight_smile:

Hmm… That does seem interesting. It doesn’t appear that that chapter is one of those made available there, so I’ll look into finding the book itself, perhaps…

also open treelib
opentreelib.sourceforge.net/

their trees look great except that lack shadowing and AO. Something the you can provide.

Thank you treeform, although I think that that’s the same link that lethe provided previously.

It does indeed look very interesting, but alas also looks dead, with no releases (aside from a nightly tarball and their CVS repository), as I recall, and little contact information (otherwise I might well attempt to contact them about it). :confused:

Update:

I at last gave flattening (specifically, flattenStrong) a shot in my little test scene, and was pleasantly surprised at the results: from frame-rates dropping to 20-30 frames per second (with v-synch on, admittedly) to never, I don’t think, dropping below 100 frames per second (with v-synch off, admittedly).

(The test scene, by the way, is the one that I described above: 50 trees, each the same model, of something around 7000 or 8000 polygons, scattered randomly over a little section of a small GeoMipTerrain, all currently untextured.)

So, the next questions that I have become these:

It has been mentioned here that I might create flattening groups on the fly, as I move around the forest. However, how often would I be likely to manage to re-create a flattening group without whatever overhead that incurs becoming problematic? And how would I mesh that with level-of-detail changes as I move about?

I think these are the sorts of questions that make this sort of thing hard to solve well. I’m not sure if anyone can answer them for you, the best answer might be, “try it and see, and let us know what you figure out.” :slight_smile:

For the record, there is a method called loader.asyncFlattenStrong(), which can flatten a model in a sub-thread. For this to work, of course, you will need a version of Panda that was compiled with threading enabled. The CVS version will be compiled with SIMPLE_THREADS enabled by default, which is a form of threading, and it might be good enough for your purposes; but if you have a multicore machine you might want to take advantage of true threading, which will give you a smoother frame rate but will impose a greater overall overhead on everything (and it will also require a custom compilation of Panda).

David

Heh, fair enough, and thanks. ^^;

Thank you too for the mention of the asynchronous version of the method - that might come in handy.

However, I find that I have another point of uncertainty, I’m afraid. ^^;

As I understand it, flattenStrong will result in my having a single NodePath where once I had many, which seems to me as though it might present a problem (forgive me please if I’m a little vague; I’m tired at the moment, I’m afraid). As I move around, various trees should change level of detail, which seems as though it would either result in my “breaking” flattenings in order to change the level of detail on some of the trees within, or change levels of detail of entire chunks at a time, replacing the entire flattened node with one at a lower level-of-detail. I’m not sure that the former is an available option under Panda, at least when using flattenStrong, while the latter seems to me as though it should either result in rather obvious changes in level of detail, since large groups could end up changing all at once, or call for smaller groups, and thus poorer performance. :confused:

Should I perhaps look at flattenMedium, which I seem to recall doesn’t result in collapse into a single node? Given its description, I’m not sure that it would result in much of a performance benefit, in my test scene, at least, since I don’t think that I had any container nodes other than the one that contains all of the trees, on which I currently call flattenStrong, leaving only the application of their individual transformations. :confused:

Well, you’ve overstated flattenStrong() a bit; it doesn’t necessarily reduce the scene graph to just one node, but it does come as close to that ideal as it can, barring other limitations imposed within the scene itself.

You’ve also understated flattenMedium() a bit. It can still have substantial value, even though it doesn’t combine nodes together quite so aggressively as flattenStrong().

To solve this problem most effectively, though, will probably require some combination of flattenMedium() and flattenStrong(). You could also do it entirely with flattenStrong(), provided you are careful.

You do understand the fundamental limitation of flattenStrong(): it tries to reduce the graph you give it into a single cullable object. But you can work with that to produce the ideal graph you want. Flatten together the groups of trees that are further off in the distance. When you come closer to those, yes, you might need to split them apart again. You can’t reverse flattenStrong(), but you can keep a copy of the trees from before you flattened them. You could switch that copy back in, and flatten them again as appropriate for the new grouping.

David

Hmm… Thank you for that; you make good points, I believe (the last especially - I feel silly for not having thought of that (especially since it seems to have been mentioned previously ^^;; ). I plead tiredness! ^^; ). :slight_smile:

I believe that I’ll think on this further, and may well post back once I’ve hopefully made some progress.

Thank you again! :slight_smile:

Update:

I’ve made a little progress (although I haven’t put in as much work as I might like, I’m afraid), and am reasonably happy thus far.

I’m rather happy with FadeLODNode - it seems to be both quick and effective, although I have seen a few z-fighting artefacts under certain conditions. I’m also impressed by the performance and effectiveness of the dual and binary transparency modes, which seem to work well for nearby leaves.

More importantly to me, the system (as it stands thus far) seems to be capable of handling a scene containing 400 trees; I doubt that I’ll want more to be around at any one time, since one is unlikely to see much further than that allows (if at all), for now at least, and I can simply insert and remove trees from the rendered scene as the player moves about the forest.

At the moment the models are still test models, all the same, lack much detail in the lower level of detail and lack a billboard level of detail.

Nevertheless, I’m encouraged. :slight_smile:

However, I do have a few issues:

  1. I recently had a shot at including normal mapping in my highest level of detail, but found that my frame rate dropped unpleasantly and the game seemed to become less responsive - which I attribute at the moment to the numerous messages outputted in SPE stating: “:pgraph(error): Shader Generator does not support ColorScale yet”. And indeed, the normal mapping doesn’t seem to fade out, just disappearing as its level-of-detail is removed.

  2. As to flattening, I find that flattenStrong seems to be a slightly lengthy process - taking long enough that flattening during play seems to produce a noticeable drop in frame rate (especially, of course, if I attempt to do it on a per-update basis). On the plus side, I seem to recall that I found that simply flattening the individual trees seemed to produce results similar to that of flattening a group of trees, which as allowed me thus far to simply flatten the trees at startup. Since, as I recall, flattening applies transformations as part of the process, I wonder how far this will let me get away with cloning trees and simply translating, scaling and rotating them, however…

  3. Finally, is there by any chance a way to output the minimum and maximum recorded frame rates, and perhaps a few statistics (percentage of time spent under x frames per second, as a possible example), similar to what I seem to recall was given after running F.E.A.R’s graphics test? If not, I may well end up heading over to the feature requests thread to request them as an optional part of the frame rate meter, or perhaps a separate tool in the same vein…

Note: I’m using Panda version 1.5.3, I believe; if any of these issues are not present in 1.5.4, please let me know. :slight_smile:

For the record, the Z-fighting artifacts of FadeLODNode have been reduced somewhat on the cvs trunk, though this won’t be part of the 1.5.x branch. As to your other issues:

  1. Sounds pretty unfortunate. Clearly, someone needs to implement ColorScale support in the shader generator. This would mean extending it to generate the appropriate shader code in the presence of this attrib. Of course, the FadeLODNode uses ColorScale to effect its fade, so until this is implemented, it seems that you can’t use the shader generator in conjunction with a FadeLODNode. For the purposes of testing, you might try substituting a normal LODNode, to see if that causes the same performance degradation.

  2. flattenStrong can indeed be expensive. Flattening the individual trees beforehand is a good idea, and can still yield substantial benefits; it might even be all you need to do (the runtime transforms applied to individual trees may not be so bad). You can also try asyncFlattenStrong (but this will require either building your own Panda, or waiting for 1.6).

  3. You can certainly write a Python task to query globalClock.getDt() every frame, or globalClock.getAverageFrameRate() and/or globalClock.getMaxFrameDuration() every couple of minutes or so, and write your own log file or statistical analysis. It wouldn’t be a very complex task. :slight_smile:

David