Skip to main content
GameDev.net gamedev.net
🔒 Locked

Volumetric clouds

Started by ZealousEngine Dec 18, 2007 at 12:36 AM 22 replies 13k views
Original Post
ZealousEngine
ZealousEngine
So ive been reading several papers, and have decided to take a stab at dobashis 'cellular automation' method. However, I have a couple questions about rendering volume data in general. It seems like when you render any volume data, you just position a billboard at each voxel. Easy enough. My question is, how do you store/manage this billboard geometry? Cleary you cant do a single draw call per billboard/voxel, so I imagine you create a vertex buffer for several dozen billboards and render them at once. But without monkeying with the vertex buffer every frame, how do you position the billboards at the correct voxel? Or perhaps you HAVE to lock the vbuffer every frame when rendering these kinds of things? Or is there some other 'trick'? I skimmed a couple papers on volume rendering, but couldnt find a detailed answer to this question. Thanks for any info! *btw if anyone has any tips/tricks for volumetric cloud rendering id love to hear that as well!
TheKrust
TheKrust
Guess I'm not sure how much this would help you with clouds, but in general volumetric effects, you may want to invest in the use of soft particles.

I guess I'm not sure why most people think soft particles are just a marketing term for DX10 (because in all reality they're possible in anything with a pixel shader). Soft particles aren't too good for things like volumetric fog, but can make very realistic and natural looking effects like smoke, dust, fire, ect.

The method I use (there are probably other ways) is by rendering a depth view from the camera position (only solid objects and not particles), an easy task if you've used shadow maps. Then I project the depth texture from my viewpoint onto the particles. The depth value of the particles is compared to the depth from the texture, and if they are close together, the pixel becomes more transparent.

The end result is particle effects that have no artifacts what-so-ever. The edges are smoothed where the billboard intersects with hard geometry and removes those ugly "lines" that are so common with particle systems.
---------------------------------------- There's a steering wheel in my pants and it's drivin me nuts
Vilem Otte
Vilem Otte
Well, there's one nice solution to do this. Use realtime raytracing, where you can nicely simulate volumetric objects. The implementation isn't nothing easy, it's damn hard to do it in realtime, but probably the best solution to do this.

Soft particles aren't bad solutions, they're quite easy (comparing to realtime raytracer) to implement. More about soft particles - look here :
http://www.devmaster.net/forums/showthread.php?p=53449#post53449
ZealousEngine
ZealousEngine
Akk raytracing.. ill sacrifice visual quality before I sacrifice performance (these clouds HAVE to animate & render fast).

Ill look into soft particles, thanks for the tip. I read a interesting tidbit about volumetric lighting here...

http://levelofdetail.wordpress.com/2007/10/04/volumetric-particle-lighting/

Is this what youre doing via rendering the 'depth' map (from the lights pov)? During this render pass, how are you rendering the voxels though (alpha blended billboards, if so doesnt that kill your fillrate)?
ZealousEngine
ZealousEngine
Hmm correct me if im wrong, but it seems the only 'thing' about softparticles is - they avoid hard clipping edges, by alpha fading out based on a depth map. Thats really not the problem im having with volumetric clouds. The two problems I see with volumetric clouds are...

1.) How do you store the GEOMETRY for the voxels? Is it a giant vertex buffer that you manipulate every frame (to position a single billboard per voxel?)?

2.) It seems like the main performance problem with volumetric clouds is fillrate. If you render EVERY voxel as a alpha blended billboard, your performance is going to suck. Is there no way around this?

*ok how about THIS to solve the 'fillrate' problem. Render JUST the closest and farthest voxels from the lights pov. So just the 'shell' of the clouds (non of the internal billboards will be rendered, saving TONS of fillrate). Now you have a depth map (which you needed if you wanted your clouds to cast shadows on the ground), AND you have a 'density' map describing how 'thick' each portion of the cloud is, which can be used for lighting.

Think this would work?

[Edited by - ZealousEngine on December 18, 2007 4:38:24 PM]
laeuchli
laeuchli
Have you read Mark Harris's article, or the Cloud article in ShaderX3?
taby
taby
There is a chapter in GPU Gems 3 that covers fluid simulation and the related visualization algorithms. The animation looks smooth, but would probably take a powerhouse to render at HD resolution. It shows how to render smoke, flame, liquid.

High-Speed, Off-Screen Particles by Iain Cantlay in GPU Gems 3 might help too.

As mentioned by laeuchli, Harris also has smoke/cloud related papers -- and an article in GPU Gems 1, 2 and 3. :)

Jos Stam also has papers on this subject, and is the originator of no less than stable fluid simulation itself. I believe that the fluid simulation component of Autodesk Maya is by Stam & company.
ZealousEngine
ZealousEngine
Well I got some gift cards for christmas, so I ordered gpu gems 3 (I have the first one, it was pretty good).

While I wait for it to arrive, ive been taking a closer look at Ysaneya's implementation. He says here in his journal...

http://www.gamedev.net/community/forums/mod/journal/journal.asp?jn=263350&cmonth=8&cyear=2004

That hes also using a 'cellular automation' method like the one in dabashis paper. However im still a little fuzzy on how you RENDER such a grid of voxels. Do you have one giant vertex buffer, with a quad/particle for each voxel in the grid? That seems like a huge waste, considering only a small fraction of the voxels actually contain a 'cloud particle'. Or is there some way to you tell a particular quad/particle not to render if there is no cloud at that voxel (or to render 100% transparent at least).

Also, with these kinds of methods it seems like the fillrate will kick your ass. I think he mentioned some way of rendering front to back, and using some kind of alpha rejection (determined by the 'density' at a particular voxel) to save some fillrate. Am I close?

One other thing in regards to shading, it looks like Ysaneya is using normals to do his shading. Wouldnt it be easier to render a 'density map' to determine shading? You can use this map for casting shadows on the terrain too. Its described here...

http://levelofdetail.wordpress.com/2007/10/04/volumetric-particle-lighting/
Ysaneya
Ysaneya
Quote:
Original post by ZealousEngine
That hes also using a 'cellular automation' method like the one in dabashis paper. However im still a little fuzzy on how you RENDER such a grid of voxels. Do you have one giant vertex buffer, with a quad/particle for each voxel in the grid? That seems like a huge waste, considering only a small fraction of the voxels actually contain a 'cloud particle'. Or is there some way to you tell a particular quad/particle not to render if there is no cloud at that voxel (or to render 100% transparent at least).


That implementation is quite old, and I'm planning to redo it (better), but from what I remember, I built an index array to skip over particles that have an alpha of 0.

Quote:
Original post by ZealousEngine
Also, with these kinds of methods it seems like the fillrate will kick your ass. I think he mentioned some way of rendering front to back, and using some kind of alpha rejection (determined by the 'density' at a particular voxel) to save some fillrate. Am I close?


Two different issues here, fillrate and sorting.

For sorting, if I remember there was a neat trick. The particles of the cloud are stored in a 3D grid. The brute-force rendering code (not sorting) is a triple loop: for each X, for each Y, for each Z. When you sort, depending on the direction between the center of the cloud and the viewer position, you need to render by +X, +Y, +Z or -X, +Y, +Z or +Y, -Z, -X or etc.. basically all the combinations of X, Y and Z, and ascending or descending. If I remember, there were 16 possible combinations. I simply precalculated 16 index arrays, calculated which one had to be used at render time by checking which component are the min/max in the direction vector.

The fillrate issue was more tricky. I ended up using impostors, rendering the cloud particles that are far away into a texture that is only updated every X frames (or when the viewpoint angle changed too much). There was all sort of problems to alpha-blend it back on the main color buffer, I had not only to render the particles into the color channels, but also the transparency into the alpha channel, and it requires the separate color/alpha blend hardware functionnality to look good.

Quote:
Original post by ZealousEngine
One other thing in regards to shading, it looks like Ysaneya is using normals to do his shading. Wouldnt it be easier to render a 'density map' to determine shading? You can use this map for casting shadows on the terrain too. Its described here...


Shading is one of the main points I want to improve now. I was using the gradiant of the cloud volume to generate an approximation for the normal, and then some magic color calculation to shader the particles. Each particle got a single color value. I'm planning to improve this by using normal mapped particles and determining the color per pixel.

I was already casting shadows on the terrain, by rendering the cloud particles as seen from the sun viewpoint into a "lightmap" texture, and then projecting it back on the terrain. This lightmap was updated every N frames, and was always centered around the camera. With a 2048^2, because cloud shadows are very fuzzy, you don't need a high resolution and you can cover a pretty large world-space area (tens, if not hundreds of Km).

Y.
ZealousEngine
ZealousEngine
Quote:
That implementation is quite old, and I'm planning to redo it (better), but from what I remember, I built an index array to skip over particles that have an alpha of 0.


Hmm can you give any insight to your new 'better' method? Dabashis paper is pretty old.. are you using any new papers in your implementation?

Also, how are you 'skipping' particles that have 0 alpha. Are you modifying the vertex buffer each frame for you 'billboard group'? Or perhaps you ARE using a quad for each voxel, but the pixel shader does a early alpha rejection check before shading?

Quote:

The fillrate issue was more tricky. I ended up using impostors, rendering the cloud particles that are far away into a texture that is only updated every X frames (or when the viewpoint angle changed too much). There was all sort of problems to alpha-blend it back on the main color buffer, I had not only to render the particles into the color channels, but also the transparency into the alpha channel, and it requires the separate color/alpha blend hardware functionnality to look good


But how are you "using imposters"? I could see how you could make imposters for individual clouds, but it seems like your clouds are part of a complete simulation. How can you use a imposter to represent a complete simulation? Perhaps youre using a big 'backdrop' imposter?

*using multiple index buffers to do the sorting is a neat trick, ill use that for sure!
Brisco
Brisco
Quote:
It seems like when you render any volume data, you just position a billboard at each voxel. Easy enough. My question is, how do you store/manage this billboard geometry? Cleary you cant do a single draw call per billboard/voxel, so I imagine you create a vertex buffer for several dozen billboards and render them at once. But without monkeying with the vertex buffer every frame, how do you position the billboards at the correct voxel? Or perhaps you HAVE to lock the vbuffer every frame when rendering these kinds of things? Or is there some other 'trick'?


Maybe you can find the answer within my diploma thesis. Just take a look at
http://www.cg.tuwien.ac.at/research/publications/2007/fizimayer-2007-art/

The method is implemented for shader model 3 hardware; improvements for DX10 hardware could be easily implemented.
ZealousEngine
ZealousEngine
Well if I was targeting sm3, this is how I would do it - setup 4 verts for each voxel. So if my voxel grid was 16x16x16 I would have a vertex buffer with 16x16x16x4 verts. Now each '4 vert quad' is actually compressed to a single point (the screen alignment is done in the vertex shader), so using a vertex texture fetch, you could tell which voxels actually have a cloud. If a grid doesnt have a cloud/particle, you simply skip he 'quad allignment' part of the vertex shader.

With this method you always have enough quads for a 'worst case' scenario (fully overcast sky). And you never have to modify the structure of the vertex buffer. On the downside youll process a lot of extra verts, but modern cards should chew threw them pretty fast.

However, lets assume I want to target sm2 (no vertex texture fetch). The onlyway I can see would be to manually setup the vertex buffer EVERY frame, and manually tell it where cloud particles are. This seems like it would be VERY slow assuming your clouds were animating at any rate of speed (something I wamt my clouds to do).

*or maybe I could use sm2, and just pass in the 'what voxels have clouds' data via a couple 4x4 matrices?

I have a feeling im missing something though...
bjarnia
bjarnia
There is an article in Game Programming Gems 5 that has the coolest looking skies I have ever seen.

It's called "Realistic cloud rendering on modern GPUs"
by Jean-Francois Dubé, Ubisoft

____________________________Bjarni Arnasonbjarni.us
ZealousEngine
ZealousEngine
Awe dont tease us, give us some details :p
bjarnia
bjarnia
I don't remember details, but here is a demo that is located on the CD:
http://www.steik.org/dump/TestSky02_HighRes.avi
____________________________Bjarni Arnasonbjarni.us
ZealousEngine
ZealousEngine
Those clouds arent volumetric. They look like simple perlin noise clouds. I want to be able to fly up through my clouds.

Im still having trouble seeing how you RENDER volumetric clouds. How do you setup the vertex buffer? How do you ANIMATE volumetric clouds without having to completely destroy/remake the vertex buffer every frame, ect..
Brisco
Brisco
Quote:
Those clouds arent volumetric. They look like simple perlin noise clouds


Yes, they are created using perlin noise. Fast animations do not look very impressive using this approach, since it is not physically based. The simple transition rules introduced by Dobashi et al. or the fluid dynamics described by Harris will give you much better results if you want to realize fast cloud simulations; for slow animations, perlin noise should be sufficient.
Nevertheless, they CAN BE volumetric, even if they are not in Dubé's implementation. Basically, the noise map values are interpreted as height information (just take a look at figure 2.13 of my thesis). This, of course, leads to the limitation, that clouds are always identical on the top and bottom half, but it may be still ok (don't have tested it). To render them in a way that makes fly-throughs possible, you can simply use a box instead of a plane during raycasting. Of course, you will need some clipping to make sure that the side plane(s) of the box are closely in front of the viewer if the camera is located within the volume. Raycasting can be performed as described in http://medvis.vrvis.at/fileadmin/publications/CESCG2005.pdf

I have also tested a combination of these methods - the cloud volume was generated as described in my thesis, using Dobashi's cellular automaton technique and rendering was performed using an improved version of the paper mentioned above. The problem is, that raycasting is really expensive and costs explode for higher screen resolutions, so the framerate drops dramatically if you switch to a resolution that is slightly higher than the previous. That is the main reason why I decided to ignore shader model 2 and below and concentrate on 3.0 hardware, where vertex shader texture fetches can be used to decide if a sprite needs to be drawn or not.
ZealousEngine
ZealousEngine
Quote:
That is the main reason why I decided to ignore shader model 2 and below and concentrate on 3.0 hardware, where vertex shader texture fetches can be used to decide if a sprite needs to be drawn or not.


Interesting, so the 'idea' I posted earlier about using vertex texture fetch to determine what quads to render wasnt a bad idea? So can you confirm you are setting up a STATIC vertex buffer with one quad per voxel (and using veretex texture fetch to determine which quads are 'active')? Is that the 'best' way to do it?
Brisco
Brisco
Quote:
Is that the 'best' way to do it?


I think it is the best way under some special conditions. For shader model 4.0, it would be possible to generate the necessary geometry on-the-fly instead of using a static buffer for the worst case. It may also be possible to improve the method using impostors as described by Harris. Nevertheless, the method works well even for large volume datasets (for example 256x32x256 voxels) and allows a high degree of freedom.

But just one comment more: you may need two static vertex buffers, one with vertices aligned to the XZ plane and one with verts aligned on the XY plane. Depending on the view angle, you can then choose the right vertex buffer to minimize artifacts which will of course occur because of incorrect drawing order.
ZealousEngine
ZealousEngine
Well Ysaneya actually suggested using one static vertex buffer, but multiple index buffers to make sure your draw order is correct. That seems like a pretty elegant solution.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.