I like seeing the thought process by this person trying to explore different strategies based on their existing knowledge. There appears to be a critical point in the article where the author makes a plot of the noise outputs and says it resembles a "bell curve", but they don't attack that as the root problem.
I can immediately see myself 10 years ago following this same thought process of how can I mitigate-this-problem, instead of dismantle-this-problem... I continually read HN and keep trying to learn new ideas so that I have ammunition for future problems.
Now a'days I would have done something similar (taken a histogram), and seen that it appeared Gaussian. I may have also done a test for normality (probably Anderson-Darling). Seeing that it is normal-ish, I would have looked up how to convert a Gaussian distribution to a Uniform distribution using the Cumulative Distribution Function.
Moral of the story - make sure to keep exploring while you're exploiting, future you will thank you.
I think ten years ago I had this problem when I was generating terrain types for a voxel world simulator (today everything with blocks is called 'Minecraft'), and I was trying very hard to explain my problem to a statistician at my university. I never got a good answer, and it would be interesting to try to go back and solve this in a better way. One of the hardest problems is generating smooth edges between terrain types, as they can be both hard and too wide at points.
I think I would try to solve this by having the angle point towards the gradient. (It's been a long time since I took a calculus class, so I hope I'm using the terminology correctly.) Basically, pick whatever direction points towards the steepest increase in value (or the greatest decrease would work too).
A possible downside depending on what you're doing is that you'd end up where local maxima in the perlin noise have arrows pointing in but not out. (I think this is what a sibling comment is referring to as "divergence".)
Yes, the standard solution to this is to use the curl if the scalar valued noise field. This gives you a vector field which is perpendicular to the gradient and i divergence free.
> The concept was to get one noise value from one area of the Perlin noise field, and another value from another part, perhaps using 3D Perlin noise. Now you have two random values ranging from -1 to +1. Using Math.atan2 on those two values will give you an angle, which should be pretty uniformly distributed between -180 and 180 degrees. And it kind of works:
> The distribution is … ok? But tons of clumping and really hard transitions in some cases. Like in the bottom left when it goes from about 45 degrees (light orange) to around 225 degrees (dark blue). My guess is that those are both very low values like 0.00001 and 0.00001 and just a tiny, tiny change to each of them makes them go to something like -0.00001 and -0.00001. But that’s 180 degrees different in terms of angle. And near the bottom center there are four cells which point in almost four completely different directions. So I took this experiment as a failure.
Combining two independent gaussian-distributed variables into a vector and taking the angle is a good way to get a uniform angular distribution, but this experiment shows that this is not enough to get a good-looking vector field, because the neighborhood structure also matters.
Points that all surrounding vectors point away from correspond to sources that push away nearby matter. That's not necessarily unrealistic, but looks weird in the middle of nowhere, especially if the point doesn't also act as a visible source of particles. Mathematically speaking, the vector field at such a point has positive divergence, and to realistically model most fluids, you'll want divergence to be zero everywhere instead.
> Perlin’s eponymous noise function [1985; 2002] is frequently used
in practice to generate random velocity fields; however, these fields
generally contain many sinks (“gutters” where particles accumulate) since they are not divergence-free. The divergence-free condition, ∇·~v = 0, is equivalent to stating the fluid is incompressible,
and is one extremely important visual characteristic of everyday
fluids (e.g. water, air, and smoke).
Their solution is to use Perlin noise as a potential function and to use the curl for the flow:
> A classic vector calculus identity is that the curl of a smooth potential is automatically divergence-free: ∇·∇× ≡ 0. Thus the velocity
field we have constructed is divergence-free, ∇ ·~v = 0, i.e. it is incompressible. No sources or sinks (“gutters”) are possible.
Variations on the Bridson paper you linked are commonly implemented in games and graphics circles under the name "Curl Noise" - for example Houdini[1] or Unity[2]
Google that term for many examples, but [3] looks like a good starting point.
Yeah, divergence-free noise is definitely what you want to look into here. It should also help with the particles collecting into strings like in your tests.
How about integrating/summing values? Seems like if we interpret the Perlin noise values as "how much to change" instead of an absolute value we would get the end result immediately.
However, it would be more difficult to determine a value just at a given point instead of walking first through all preceding points.
It seems that they want a smooth random vector field. Why don't they build that, which is easy to do? Why do they want, specifically, a field of angles? You cannot average angles, and smooth noise, being computed by local averaging, is not going to work well for angles. But you can average vectors.
I can immediately see myself 10 years ago following this same thought process of how can I mitigate-this-problem, instead of dismantle-this-problem... I continually read HN and keep trying to learn new ideas so that I have ammunition for future problems.
Now a'days I would have done something similar (taken a histogram), and seen that it appeared Gaussian. I may have also done a test for normality (probably Anderson-Darling). Seeing that it is normal-ish, I would have looked up how to convert a Gaussian distribution to a Uniform distribution using the Cumulative Distribution Function.
Moral of the story - make sure to keep exploring while you're exploiting, future you will thank you.
Edit for readability.