Actually what I have in mind is value noise I think, but what I am going to ask applies to both of them.

It is known that if you want to produce tiled texture by using the perlin/value noise, the size of the texture should be specified as the power of 2 (2^n). Without any modifications to the algorithm when you use the size of (2^n)+1 the texture cannot be tiled anymore, so I am wondering whether it is possible (by modifying the algorithm somehow) to generate such tiling texture with the size of (2^n)+1. The article (from which I have my implementation) is here: http://devmag.org.za/2009/04/25/perlin-noise/

I am aware that I can produce texture with 2^n size and just copy twice the last column/row from the ends to make it (2^n)+1, but I don't want to, because such repetitions are visible too much.

  • \$\begingroup\$ Here's a great link to look at, where there is 2D, 3D and 4D tiling, and it's open source: accidentalnoise.sourceforge.net \$\endgroup\$ – William Mariager May 3 '13 at 17:38
  • \$\begingroup\$ Why are you using textures of size (2^n)+1? Most graphics cards will resize this to the next greatest power of two for performance issues, and there is no qualitative difference between the two texture sizes when dealing with noise... \$\endgroup\$ – Mokosha May 3 '13 at 17:43
  • \$\begingroup\$ This texture isn't actually a texture but a heightmap, where every pixel is a vertex on my terrain. I want it to be (2^n)+1 because it can be easily divisible by 2, e.g. 1 2 3 4 5 6 7 8 9, the center of it is 5, next division by 2 makes it 3 and finally 2. When I have 2^n I have 1 2 3 4 5 6 7 8, so the centre of it is between 4 and 5. \$\endgroup\$ – tobi May 3 '13 at 17:49
  • \$\begingroup\$ If it has to be tiled over a 2^n wide terrain, then no you do not need an extra row and column, you need the vertex edges to be exactly the same, otherwise it won't fit. \$\endgroup\$ – aaaaaaaaaaaa May 4 '13 at 8:55

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.

Periodic Perlin

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)
  int hash;
  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);

Here X and Y are coordinates of the grid point, width and height are the size of the grid and tilingWidth and tilingHeight are size of the smallest tileable unit (my 32x32px tiles have width and height 1, tilingWidth and tilingHeight 1, 64x64 tiles have width and height 2, tilingWidth and tilingHeight 1). seed changes interior of the noise, edgeSeed changes the edges. x and 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 p, 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, ...

  • 1
    \$\begingroup\$ Hi @cube, and welcome to GDSE. Questions on SE sites are timeless and new answers are always encouraged. Do you think you'd be able to say more about this process? That PDF could become a 404 page in the not too distant future, and if it does, this answer becomes not very useful. \$\endgroup\$ – doppelgreener Oct 4 '13 at 8:58
  • \$\begingroup\$ is that better? \$\endgroup\$ – cube Oct 23 '13 at 15:43
  • \$\begingroup\$ @cube Much better, but you don't need to keep the original answer at the bottom (the SE software automatically keeps the editing history of all questions and answers). I removed that. \$\endgroup\$ – Nathan Reed Oct 23 '13 at 17:58
  • \$\begingroup\$ Also, I think it's worth mentioning that if tilingWidth or tilingHeight get bigger than 256, you should use a bigger permutation table as well. Or switch to an actual hash function. \$\endgroup\$ – Nathan Reed Oct 23 '13 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.