The paper Procedural Textures Using Tilings With Perlin Noise describes how to solve this.
Perlin noise overview
Perlin noise made by taking a regular square (or cube / hypercube) grid and selecting
pseudorandom gradient vectors in the corners of this grid.
Output of the noise is then based on the gradient vectors of the four nearest grid corners.
(A little detail that was confusing me for a long time -- In all implementations I've seen you never calculate the gradient vectors directly, instead dot product of the gradient vector and a vector from the examined point is calculated directly).
Large part of Perlin noise implementation consists of hashing the point positions to obtain the gradient vectors. Because this operation starts by anding coordinates with 0xFF, the common implementation of the noise is periodic every 256 units.
In the article they make the noise tileable by making sure that the first column of gradient vectors is the same as the last one, and that the top and bottom row are the same.
I must admit that I didn't read the article too carefully, but I think they are explicitly building a grid with gradient vector indexes, and copying the rows and columns where necessary. Also they use colored edges for Wang tiles.
In my implementation I used a simple modulo operator calculated on the fly + some additional magic (I needed to make multiple sizes of noise (32x32px, 64x64px and 128x128px), each tileable with all sizes).
This is how the relevant part of my implementation looks like:
float grad(int X, int Y,
int width, int height,
int tilingWidth, int tilingHeight,
int seed, int edgeSeed,
float x, float y)
if (X % width == 0 || Y % height == 0)
hash = p[p[p[X % tilingWidth] + Y % tilingHeight] + edgeSeed];
hash = p[p[p[X] + Y] + seed];
return ((hash & 1) ? x : -x) + ((hash & 2) ? y : -y);
Y are coordinates of the grid point,
height are the size of the grid and
tilingHeight are size of the smallest tileable unit
(my 32x32px tiles have
tilingHeight 1, 64x64 tiles have
seed changes interior of the noise,
edgeSeed changes the edges.
y are the input vector that is getting dot-producted with the gradient vector.
The hashing trick (array of twice repeated permutation of 256 values in
hash = p[hash + nextByte]) was copied from the regular Perlin noise as well as the final line that outputs the dot product.
Once this single octave noise is done, it is easy to add multiple octaves.
Only thing that must be taken care of is to increase the period as the noise is scaled
to higher frequency.
value += singleOctave(x * frequency, y * frequency,
width * frequency, height * frequency /* THIS!! */
tilingWidth * frequency, tilingHeight * frequency /* and THIS!! */
/* ... */) * amplitude;
The seed does not has as much influence on the noise as it would have for a regular noise. This is because the large low frequency waves are almost completely determined
by the edges.
Width and height must be integers. This is mostly a problem only for non-square tiles.
with aspect ratios different than 1/2, 1/3, 2/3, ...