@zep, I've been really curious about the water feature in Voxatron. I love how it looks and how it acts. It just looks and feels so satisfying. But I've had just a hell of a time finding good articles that give me any sort of clue on how to recreate it with any sort of decent performance. (Or even to look as good as yours.) There's the classic water ripple algorithm that so many articles refer back to. But running that on 128^2 voxels just kills performance. Any clues as to what direction to look in to do water the way you do it in Voxatron? Is it something that works well because it's done "natively", so to speak, but wouldn't necessarily work well within the Lua performance constraints of the custom scripting feature?
Bring the resolution down. that's 16384 iterations. Try calculating 1/2 or 1/3 of resolution then scaling. Then optimize the lua doing the work. Post it if you don't mind can see what can bring profile time down. I don't have time to tinker with it but I'm interested.
Altogether the algorithm could probably be cut down quite a bit from 16k iterations. This water simulation should only be attempted at 30 per sec or 60 per second tickrates to start. It is technically physics.
This said, I'm sure there are much more basic water simulation scripts that would do just fine in Vox. This looks like a simulation-quality script - more mathematical if anything. Good luck.
TLDR - Lua doesnt run simulation quality realtime calculations well.
Yeah, I actually did lower the resolution down to 64^2 and it still wasn't super performant. Also, more importantly, that particular algorithm didn't look great in voxels. It looked fine in PICO-8, but didn't have the same juicy feel in Voxatron that the native Voxatron water has.
@MBoffin Voxatron also uses that reference code! It runs on a 512x512 grid at 15fps. The plan was to calculate it in slices, but implemented in C it is easily fast enough. Unfortunately it would very cpu intensive otherwise -- I can't imagine anything larger than a 64x64 grid working in Voxatron/PICO-8 Lua.
In terms of behaviour, one thing I added is a method for altering the surface of the water that doesn't introduce harsh discontinuities. In Voxatron's case, I raise the values inside a square (most objects look squareish) and then smooth that area using an iterated blur. I do this in both the 'current' array and the previous frame array.
This general method of doing water is extremely sensitive to large differences between neighbouring values (in space and/or time), and quickly becomes unstable otherwise.
Voxatron uses 32-bit fixed point numbers, but there is also still a lot of noise after ripples have died down. It never actually reaches zero again -- I just make sure it is within half a voxel high so that it isn't visible. It might look like there is a nice cute algorithm running underneath but unfortunately it was mostly trial and error hackery.
The problem is worse with 8-bit values, of course. I tried a demo in PICO-8 using peek/poke'd for quick access to arrays. 128 means 0. But the problem is dampening -- it is very hard to nudge everything back to 128 without prematurely killing off ripples. Try changing the 0.5 on the following lines to something like 0.25 (or 0) to see what I mean:
if (q>128.25) q-=0.5 if (q<127.75) q+=0.5
Press x to cycle through 3 rendering modes: heightfield raw value, simple lighting, vaporwave grid. Mouse to draw, tab to see the framerate.
Perhaps could try a 64x64 16-bit values version using peek16() to see how much that helps dampening, but it would need 8k of base ram.
That's really helpful! Thank you!! :) Also good to know I was on the right track with that original article, but the extra information you provided helped me understand why I wasn't seeing similar results visually and performance-wise. (Also, I love the look of the vaporware grid.) :D
[Please log in to post a comment]