Sign in to follow this  
RobMaddison

Terrain PVS

Recommended Posts

RobMaddison    1151
Hi all I'm about to implement some kind of PVS into my engine. My terrain rendering algorithm is already pretty efficient (albeit somewhat reliant on the CPU), but I'd like to improve it further by culling more chunks away than my current basic frutum culling. I've been looking at articles on the net but haven't really found anything yet that describes any kind of implementation that well. I did have this rough idea that I'd like to share to see if anyone can see any value in it (or whether it can even be done): My terrain is currently 4096x4096 and is split up into 256x256 chunks. That 256x256 chunk doesn't contain 256x256 vertices as LOD is carried out within the hierarchy of each chunk. Each DrawPrimitive call is on either one of these 256x256 chunks, or a further away low level 512x512 chunk. What I'd like to do is dynamically determine from a viewpoint whether each 256x256 chunk inside the frustum is visible behind any possible occluding geometry. For each terrain chunk, we store a very low detail 'covering' representation of that chunk (say 5x5 vertices). By 'covering' representation, I mean if you draw the terrain chunk at the highest detail and then drew the very low cover over the top, none of the high level detail underneath would poke through, but the low-level cover would give a fair representation of peaks, hills, etc. Each chunk to be drawn that exists within (or partly within) the frustum is then depth sorted. Before each chunk is drawn (starting with the closest), the very low-level geometry for that chunk is drawn to a copy of the render surface. If the z-buffer of that copy surface has changed, the normal level chunk is properly drawn on the original surface. If the z-buffer didn't change at all (not even sure if this can be checked), then we know that chunk can be completely ignored as none of it can be seen (if its cover can't be seen, the underlying high-level data can't be seen). After each chunk is drawn, we check the z-buffer to see if it has been completely filled (don't know if this can be done either). If it has, we stop rendering as we know nothing else can be seen (if you're facing a mountain face for example). I know this might seem like a lot of work when you can pre-compute, but it means that the PVS system can be dynamically updated with terrain changes. As long as the low-level covering polys move with the underlying geometry, the PVS should still work. I don't know an awful lot about the z-buffer, so this method would be dependent on: a) you can check after a DrawPrimitive call to see if anything on the z-buffer was changed. b) you can check to see if every pixel on the z-buffer has been written to c) you can (quickly) make a copy of the render surface as it currently is and render to that per DrawPrimitive call (for my current terrain, there could be between 1 and 256 DrawPrimitive calls depending on the complexity of the terrain). Does it sound like there might be some value in an approach like this (with any suggested changes, etc)? If not, can anyone suggest some more in-depth reading of how terrain PVS is done? Thanks

Share this post


Link to post
Share on other sites
Trond A    139
Hi!

For a real-time occlusion culling system, I think you are onto something. You should check out hardware occlusion queries, which basically lets you know how many pixels were affected by your drawcalls (sounds like this is what you wanted by reading the z-buffer, except that is probably too slow to do in software to give you any benefit).

nVidia has an article on it here: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter06.html

-Trond

Share this post


Link to post
Share on other sites
RobMaddison    1151
Thanks, Trond

I read that article, it's very interesting, but the asynchronous nature of the the hardware occlusion query concerns me a bit. The temporal cohesion idea is pretty neat, but I'm not sure if it would be suited to fairly fast-moving terrain.

I'll keep on searching.

By the way, does anyone happen to know how the Crysis engine does its occlusion culling? It's dynamic from what I can tell from the editor.

Cheers

Share this post


Link to post
Share on other sites
Trond A    139
If the queries were not asynchronous, they would stall the GPU, so it is actually a good thing (as you could end up with better framerates with just drawing the stuff you would test for occlusion, in many cases).

I don't know if you have heard of this: http://www.umbra.fi/index.php?products&umbra

It is a real-time occlusion culling middleware, and as far as I know, they make heavy use of occlusion queries. I'm not sure what Crysis does, but I would be surprised if they're not using them as well. If you don't want to do that, you would probably need to pre-compute the occlusion in some way or another (be it completely offline, or whenever needed in-game).

Keep us posted on your research, this is definately an interesting topic :)

-Trond

Share this post


Link to post
Share on other sites
RobMaddison    1151
I read over the GPUGems document again on harware occlusion theories and I'm a bit confused about something.

It interchanges rendering bounding boxes with rending the actual geometry. I can understand rendering with bounding boxes when you execute an occlusion query, but surely you must render the _actual_ geometry as soon as you know the patch is visible otherwise future bounding box queries that may lie underneath this patch won't be accurate because they're tested against a bounding box.

I hope I explained this right, it's a tricky subject to describe.

As an example, if you issue 2 queries for 2 objects (for argument's sake) and the second of these objects is partly occluded by the first, how can the second query run accurately if it is being checked against the bounding box of query 1? Surely it needs to be checked against the actual geometry rather than the bounding box.

I guess to simplify my question further, how can you continually issue occlusion queries (which are essentially bounding box renders of each object), without the GPU knowing (prior to each query) what exactly is in the z buffer? In the following (badly drawn) image:


_______
__________/
__ / /
\/ ob 2 / ob 1
\ /
\______/



Object 1 and object 2 (terrain patches, say) are sent as hardware occlusion queries (as bounding boxes), but the bounding box of object 1 completely occluds object 2 whereas the actual geometry (shown) doesn't.

I hope I've made sense and someone can shed some light.

Thanks

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this