9 Answers
9

There's two parts to making seamlessly tileable fBm noise like this. First, you need to make the Perlin noise function itself tileable. Here's some Python code for a simple Perlin noise function that works with any period up to 256 (you can trivially extend it as much as you like by modifying the first section):

Perlin noise is generated from a summation of little "surflets" which are the product of a randomly oriented gradient and a separable polynomial falloff function. This gives a positive region (yellow) and negative region (blue)

The surflets have a 2x2 extent and are centered on the integer lattice points, so the value of Perlin noise at each point in space is produced by summing the surflets at the corners of the cell that it occupies.

If you make the gradient directions wrap with some period, the noise itself will then wrap seamlessly with the same period. This is why the code above takes the lattice coordinate modulo the period before hashing it through the permutation table.

The other step, is that when summing the octaves you will want to scale the period with the frequency of the octave. Essentially, you will want each octave to tile the entire just image once, rather than multiple times:

im not a python guy, so i ask, how does x*2**o convert to C? is it: x*pow(2,o) or pow(x*2,o) ?
–
idevFeb 11 '12 at 14:49

4

x*pow(2, o), since exponentiation has higher precedence than multiplication.
–
John CalsbeekFeb 11 '12 at 19:05

could someone convert this to C? i have huge problems understanding this code, as i have never done anything with python. for example what is a value? and im not sure how the functions convert to C... i get straight lines in output only.
–
idevFeb 12 '12 at 15:06

1

This is definitely the best solution so long as you're fine with the domain of your noise being tied to the shape of your tile. For example, this doesn't allow arbitrary rotations. But if you don't need any such thing, this is the ideal answer.
–
John CalsbeekFeb 12 '12 at 22:49

Basically, map the X coordinate of your pixel to a 2D circle, and the Y coordinate of your pixel to a second 2D circle, and place those two circles orthogonal to each other in 4D space. The resulting texture is tileable, has no obvious distortion, and doesn't repeat in the way that a mirrored texture would.

Copy-pasting code from the article:

for x=0,bufferwidth-1,1 do
for y=0,bufferheight-1,1 do
local s=x/bufferwidth
local t=y/bufferheight
local dx=x2-x1
local dy=y2-y1
local nx=x1+cos(s*2*pi)*dx/(2*pi)
local ny=y1+cos(t*2*pi)*dy/(2*pi)
local nz=x1+sin(s*2*pi)*dx/(2*pi)
local nw=y1+sin(t*2*pi)*dy/(2*pi)
buffer:set(x,y,Noise4D(nx,ny,nz,nw))
end
end

thanks john! got it working, sweet! nobody said it, but: the x1,y1,x2,y2 seems to be some sort of scaling, the larger distance, the detailed noise. if this helps anyone.
–
idevFeb 10 '12 at 21:02

3

Note that this is topologically equivalent to bobobobo's answer: your mapping embeds a 2-torus into an ℝ⁴, which is possible without the metric distortions you invevitably get when embedding it into ℝ³.
–
leftaroundaboutFeb 11 '12 at 13:21

It kinda works, but it looks like you're getting a bunch of distortion due to the curvature of the torus.
–
Nathan ReedFeb 10 '12 at 21:51

you can really just modulo the position, but I love all the awesome/creative answers to this question. So many different ways to do the same thing.
–
NeuroFuzzyFeb 11 '12 at 8:57

i noticed you actually dont want to use 0-1 values, but 0-0.9999... values! so you would use: x/width, y/height etc. otherwise the seams doesnt match (makes the opposite edges exact same pixels). also it looks like the PerlinNoise3D() function needs clamping for the result value too, or some pixel values overflow.
–
idevFeb 11 '12 at 15:52

@Nathan, do you know how to fix the distortion ?
–
idevFeb 11 '12 at 15:53

2

@idev I believe the way to fix the distortion is to use the 4D method in the top answer of this question. ;)
–
Nathan ReedFeb 11 '12 at 16:24

One simple way I can think of would be to take the output of the noise function and mirror/flip it into an image that's twice the size. It's difficult to explain so here's an image:

Now, in this case, it's pretty obvious what you did when you look at this. I can think of two ways to (possibly :-) ) resolve this:

You could take that larger image and then generate some more noise on top of it but (and I'm not sure if this is possible) focused towards the middle (so the edges stay the same). It could add the extra bit of difference that would make your brain think it's not just mirror images.

(I'm also not sure if this is possible) You could try fiddling with the inputs to the noise function to generate the initial image differently. You'd have to do this by trial and error, but look for features that draw your eye when you tile/mirror it and then try and get it not to generate those.

@bobobobo That's what I was thinking the other steps would alleviate. So, you could generate a "base" using this method, and then add some more details over the whole thing to make it look like it's not (so) mirrored.
–
Richard Marskell - DrackirFeb 10 '12 at 18:29

You start to get some weird patterns when you do this kind of thing. This one in particular looks kind of like a butterfly. Easy solution, though.
–
stepheltonFeb 10 '12 at 21:04

This was my first approach too, but it has a problem, visible here: dl.dropbox.com/u/6620757/noise_seam.png As you cross a flip boundary you cause a disjoint in the noise function by instantly inverting the slope of the function. Even if you apply a second noise function on top, that may still be visible in the output.
–
JhericoNov 25 '12 at 22:57

A method I used successfully is make noise domain tiled. In other words, make your base noise2() function periodical. If noise2() is periodic and beta is integer, resulting noise will have the same period as noise2().

How can we make noise2() periodic? In most implementations, this function uses some kind of lattice noise. That is, it gets random numbers at integer coordinates, and interpolates them. For example:

But how do you make noise2 periodic (with a short period such as 1 unit)? I think that's what the question is ultimately asking. Standard Perlin noise is periodic with a period of 256 on each axis but you want a modified noise with a smaller period.
–
Nathan ReedFeb 10 '12 at 6:52

@Nathan Reed If you call noise2 as suggested, you will get periodic results, whether the function itself is periodic or not. Because the arguments wrap around every 1 unit.
–
NevermindFeb 10 '12 at 7:01

But then you get seams at the grid lines, don't you? Since there's no guarantee that noise2(0, 0.999) is anything near noise2(0, 0), unless I've missed something.
–
Nathan ReedFeb 10 '12 at 17:30

@Nathan Reed That's a good point. In fact, I just re-checked my old code and it turns out I was wrong. I'll edit the answer now.
–
NevermindFeb 10 '12 at 18:57

GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of the textures.

Of course, this method is more costly than simply having a single texture that can be used across multiple surfaces. If you are looking to create some random tileable textures, this may be something that interests you.

Though there are some answers here that would work, most of them are complicated, slow and problematic.

All you really need to do is use a periodic noise generation function. That's it!

An excellent public domain implementation based on Perlin's "advanced" noise algorithm can be found here. The function you need is pnoise2. The code was written by Stefan Gustavson, who has made a pointed comment here about exactly this issue, and how others have taken the wrong approach. Listen to Gustavson, he knows what he's talking about.

Regarding the various spherical projections some here have suggested: well, they in essence work (slowly), but they also produce a 2D texture that is a flattened sphere, so that the edges would more condensed, likely producing an undesired effect. Of course, if you intend for your 2D texture to be projected onto a sphere, that's the way to go, but that's not what was being asked for.

I had some not-bad results interpolating near the edges of the tile (edge-wrapped), but it depends on what effect you're trying to achieve and the exact noise parameters. Works great for somewhat blurry noise, not so good with spikey/fine-grained ones.

You use a modular wrap around for each scale of the noise. These fit the edges of the area no matter what frequency scale you use. So you only have to use normal 2D noise which is a lot faster. Here is the live WebGL code which can be found at ShaderToy:
https://www.shadertoy.com/view/4dlGW2

The top three functions do all the work, and fBM is passed a vector x/y in a 0.0 to 1.0 range.