cinder / Cinder

Cinder is a community-developed, free and open source library for professional-quality creative coding in C++.
http://libcinder.org
Other
5.34k stars 944 forks source link

Perlin noise 2d implementation problems #1326

Open deanm opened 8 years ago

deanm commented 8 years ago

Basically the Cinder 2D Perlin noise is just 3D Perlin noise with a hardcoded z=0. It uses the same 3D gradient dot products as 3D noise, but just again with z=0. The problem with this is that 3D Perlin noise is designed with cube gradients, and behaves especially badly at z = 0 (and many others, integers probably being worse). The 3D Perlin gradients are the midpoints on a cube, for a 2D implementation, for example, Stefan Gustavson says:

"A good choice for 2D and higher is to pick gradients of unit length but different directions. For 2D, 8 or 16 gradients distributed around the unit circle is a good choice. For 3D, Ken Perlin’s recommended set of gradients is the midpoints of each of the 12 edges of a cube centered on the origin."

Attached is an image of Perlin noise with a z of 0, see the long horizontal lines, for example at Y=-1 when X < 0. But in the image you can easily see several places where the entire edge of one or more neighbouring cells is zero across the entire cell.

If you were to do the same thing but with z = 0.1, you get something like this:

So basically, it's not the best idea to implement 2D Perlin with 3D Perlin, and if you do it should at least be better to not use an integer value of z. But really, you should implement it with a different set of 2d gradients altogether (ie distributed around a circle). This would basically just involve a new Perlin::grad( int32_t hash, float x, float y ) function, which shouldn't be a very big change.

Just mentioning @notlion and @meshula as they were part of other discussions on noise.

andrewfb commented 8 years ago

This all makes sense, and there's no doubt the Perlin implementation is in serious need of refactoring. It's likely to me that the analytic derivatives are incorrect too, though I haven't looked at it in depth. Is this something you'd be willing to help out with?

deanm commented 8 years ago

Just to leave a few more notes as a reference. Here is more of a debug image that overlays the gradients. When you remove the z component you go from 12 to 8 gradient vector, and it seems like 8 just isn't enough, since 8^4 means there are only 4096 possible cells, so there is basically just 4096 tiles (and conceptually even less because each one also has its rotations). You can already see a bit the repetition in the image.

The overlay is the gradient vector and the hash value for that corner.

perlin2d

notlion commented 8 years ago

Hey, Dean!

I see you posted in the thread over at #901, but have you tried this with GLM's perlin/simplex functions from glm/gtc/noise.hpp? I'm curious if they would fare better.

deanm commented 8 years ago

Simplex noise is something else, so wouldn't expect to see anything similar.

I don't use GLM, but a quick look at:

https://github.com/g-truc/glm/blob/master/glm/gtc/noise.inl

Make it look like it's the GPU algorithm from McEwan/Gustavson, see 5.4 here:

http://webstaff.itn.liu.se/~stegu/jgt2011/supplement.pdf

Perhaps it is so they can match the same noise across CPU and GPU implementations, but using a permutation polynomial will be a bit different than how Cinder works currently. But anyway, yes this algorithm should improve the output.

notlion commented 8 years ago

Yup. GLM's implementation is based on webgl-noise which I believe was written by Gustavson. glm/gtc/noise.hpp is included with Cinder and has both perlin (classic) and simplex noise implementations which is why I mentioned it.

meshula commented 8 years ago

Ashima's webgl-noise is great :)