Stable Fluids - Jos Stam - GDC03 code

Started by
18 comments, last by DWN 13 years, 9 months ago
(... several facepalms later...)

I started over and now use an almost 1:1 copy for my C# implementation (it really was a bad idea to switch language and refactor at the same time). Even then I encountered similar problems - until I found my stupid bugs. Now it seems to work, so on your side, Toorop, I definitively suspect either a quirk with the SWAP or something going wrong with handling your input (sources) rather than some physics going wrong. I can turn off/on the advection like you did - and/or even the whole velocity step - I get the same results as long as there are no forces added: Here I "drop" density at a random point every 4th frame (read left-to-right, top-to-bottom):

imagebam.com

Can you post your whole (relevant) code, please ?
Advertisement
Quote:Original post by unbird
(... several facepalms later...)


Thanks for letting us know. I definitely need to refresh my memory! I should be facepalming, not you.
Finally, I have my own working version of Stam's code. I have messed up the pointers in my implementation of SWAP - unbird was right! :)

The code is nothing special, just some minor name convection modifications and stuff.

I haven't implemented the velocity update part, so I'm solving just the advection diffusion equation for the non-reactive substance (dye). The problem is still here - with zero velocity field I cannot skip the advection for dye. It's a misery because advection won't do anything with zero velocities.

main loop
void onIdle( ){	updateFromUI(dyeDensity_prev, velU_prev, velV_prev);	dyeUpdate(dyeDensity, dyeDensity_prev, velU, velV, dyeDiffRate);	glutPostRedisplay();}


advection diffusion equation solver
void dyeUpdate(float* x, float* x0, float* u, float* v, float diff){	addSource(x, x0);	swapBuffers(x0, x); diffuse(0, x, x0, diff);	swapBuffers(x0, x); advect(0, x, x0, u, v);	// won't work without this but IMO it should be fine}


In the diffuse() and advect() functions I haven't changed anything - it's Stam's code.
Glad to hear it's working, sort of. Since my facepalming wasn't over yet here some pitfall you might want to look into. The naming of the globals with a _prev suffix suggest they hold the previous state, but this is only true after the update (maybe not even that). Before calling vel_step/dens_step they have to be initialized with delta values through get_from_UI - they will be added through an add_source-call at the very beginning of both parts. If no interaction is desired, the _prevs have to be zeroed, and this is where I was failing: I inserted my density/forces punctually, forgot to zero. So, the get_from_UI call is a rather important part of the simulation loop.
Little update here!
I have extended the code to support different dye colors in order to create some nice mixing effects. The below picture depicts the evolution of four smoke puffs with different colors. I add additional forces with the mouse. Pretty nice looking! ;)
imagebam.com
I implemented a basic dissipate routine to avoid the case of filling the whole simulation area.

But unfortunately, the initial question still persists and it's quite frustrating ...
Niiiiiice [cool]

I can imagine your frustration, that's what I went through yesterday. You can still post your code (or PM me), maybe I spot the bug.

Thought about dissipation, too, actually had once such a bug where my density exploded without adding mass. So I provided a function which tells me the current total mass to check for preservation.

Though I now understand the maths better, I probably won't go much further with refactoring or optimizations now, just did some minor C#-obvious changes. I mainly wanted to have a CPU-reference: This thing has to go shaders!

Thanks again to make me look into this!

unbird
I'm thankful for all your efforts - I'll send the code via a PM.

One can find a nice follow-up to the Stable Fluids method here.
Thanks to the helpful remarks from unbird I managed to get the dyeUpdate work with zero velocity field without the advection step.

In the original implementation the dyeUpdate function gets the buffers by value. The double swapBuffer call refreshes the original buffers correctly. But if only just one swapBuffer call is present the data isn't propagated back correctly. Using pass by reference in the dyeUpdate solves the whole misery!

It was a pretty lame mistake, thanks for all your comments!
Hi, at the end of that Stam paper, he states he's implemented the algorithm in 16-bit fixed point. Is this code available someplace? Anyone know?
WTH - marching cubism, Batman, this little thing is a patented algorithm?! Then why did he give everyone the algo? Is this Stam guy a sadist? Why should I much care if I can't use it? mostly waste of time then. I like experimenting and working on algorithms, but code is meant to be code, not theory.

This topic is closed to new replies.

Advertisement