Jump to content
  • Advertisement
Sign in to follow this  
Pikkolini

How do I create tileable 3D Perlin/Simplex noise?

This topic is 852 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone,

 

I have a library which provides functions for 1/2/3/4-D simplex noise. With that I successfuly created a tileable 2D cloud (fBm) texture (see How do you generate tileable Perlin noise?). Now I want to create a tileable 3D cloud volume. It only has to be tileable in 2 dimension of course.

How can I achieve this? Do I need higher dimension than 4D noise?

 

Share this post


Link to post
Share on other sites
Advertisement
If you don't need it to tile in 3 dimensions, then 5-D noise can do it. If you need it to tile in 3 dimensions, then 6 D will work.

I wrote a journal entry on this topic some time back: http://www.gamedev.net/blog/33/entry-2138456-seamless-noise/

If you don't want to use higher orders of noise, then you can use the blending method, or you can use lattice wrapping, such as one of the answers in that stackexchange thred suggest. Assuming your noise is a simple, vanilla Perlin or simplex fractal, and not anything more complex than that or including domain rotations.

The Accidental Noise Library ( https://github.com/JTippetts/accidental-noise-library ) uses the 6d method, if you care to check it out. The implementation of 3D noise mapping of various seamless modes is at https://github.com/JTippetts/accidental-noise-library

Note that the seamless method using higher orders of noise operates using some interesting domain transformation (a later poster to my journal called it a Clifford torus ( https://en.wikipedia.org/wiki/Clifford_torus ); you learn something new every day ) and so it can have consequences if you are, for example, using repeating patterns as part of your pipeline. For cloud noise it works well, but the domain transformation will skew any pattern functions beyond recognition.

Share this post


Link to post
Share on other sites

I have just had the same problem, needed 3d volume textures for some voxel test thingie (tileable in xyz) - what I did was this:

I created the texture data, then I blended in the first 1/4th of the x/y/z with the last 1/4th of the volume, mirrored.

The code is probably better in explaining it, it :


VOID LayerGraphics::Make3DTextureSeamless(ULONG *data, UINT size)
{
	UINT size_3_4th = (size * 3) / 4;
	UINT size_pow2 = size * size;
	UINT size_div4 = size / 4;
	for (UINT z = 0; z < size; z++)
		for (UINT y = 0; y < size; y++)
			for (UINT x = 0; x < size; x++)
			{
				if ((x >= size_3_4th) || (y >= size_3_4th) || (z >= size_3_4th))
				{
					UINT x0 = x;
					UINT y0 = y;
					UINT z0 = z;
					UINT x1 = x;
					UINT y1 = y;
					UINT z1 = z;
					FLOAT xi = 0.0f;
					FLOAT yi = 0.0f;
					FLOAT zi = 0.0f;
					if (x1 >= size_3_4th)
					{
						x1 = size - 1 - x1;
						xi = 1.0f - ((FLOAT)x1 / (FLOAT)size_div4);
					}
					if (y1 >= size_3_4th)
					{
						y1 = size - 1 - y1;
						yi = 1.0f - ((FLOAT)y1 / (FLOAT)size_div4);
					}
					if (z1 >= size_3_4th)
					{
						z1 = size - 1 - z1;
						zi = 1.0f - ((FLOAT)z1 / (FLOAT)size_div4);
					}
// 3D interpolation
					ULONG value0a = data[x0 + y0 * size + z0 * size_pow2];
					ULONG value1a = data[x1 + y0 * size + z0 * size_pow2];
					ULONG value2a = data[x0 + y1 * size + z0 * size_pow2];
					ULONG value3a = data[x1 + y1 * size + z0 * size_pow2];
					ULONG value0b = data[x0 + y0 * size + z1 * size_pow2];
					ULONG value1b = data[x1 + y0 * size + z1 * size_pow2];
					ULONG value2b = data[x0 + y1 * size + z1 * size_pow2];
					ULONG value3b = data[x1 + y1 * size + z1 * size_pow2];
					ULONG value_00 = InterpolateColors(value0a, value1a, xi);
					ULONG value_01 = InterpolateColors(value2a, value3a, xi);
					ULONG value_02 = InterpolateColors(value_00, value_01, yi);
					ULONG value_10 = InterpolateColors(value0b, value1b, xi);
					ULONG value_11 = InterpolateColors(value2b, value3b, xi);
					ULONG value_12 = InterpolateColors(value_10, value_11, yi);
					ULONG value = InterpolateColors(value_02, value_12, zi);
					data[x + y * size + z * size_pow2] = value;
				}
			}
}

ULONG InterpolateColors(ULONG color0, ULONG color1, FLOAT i)
{
	FLOAT iinv = 1.0f - i;
	ULONG r0 = (color0 & 0x00FF0000) >> 16;
	ULONG g0 = (color0 & 0x0000FF00) >> 8;
	ULONG b0 = color0 & 0x000000FF;
	ULONG r1 = (color1 & 0x00FF0000) >> 16;
	ULONG g1 = (color1 & 0x0000FF00) >> 8;
	ULONG b1 = color1 & 0x000000FF;
	ULONG r = (ULONG)((FLOAT)r0 * iinv + (FLOAT)r1 * i);
	ULONG g = (ULONG)((FLOAT)g0 * iinv + (FLOAT)g1 * i);
	ULONG b = (ULONG)((FLOAT)b0 * iinv + (FLOAT)b1 * i);
	return (r << 16) | (g << 8) | b;
}

It works fine for my purposes :D

Share this post


Link to post
Share on other sites

Thank you for the awesome replies :D

I think I will try the attempt with the higher dimensions first. Just out of curiosity, do you know any other 5D or 6D implementations of simplex noise for C/C++? I couldn't find any with google, not even the one you posted in this thread.

Share this post


Link to post
Share on other sites
Some years back, when I was implementing my own higher orders of simplex, I found an implementation of N-dimensional simplex noise in Python: https://github.com/Craig-Macomber/N-dimensional-simplex-noise/blob/master/simplex.py Converting it from Python would take a little bit of work, but you could probably work it out in time.

You might be able to find more by googling for "n dimensional simplex noise". The simplest variant to convert to higher dimensions is the original Perlin noise, as it simply deals with interpolating the corners of an N-dimensional hypercube. Conceptually it's simple, but algorithmically it gets complex in an exponential fashion. You can decompose the recursive structure of standard Perlin noise into polynomial form, but the higher orders get pretty sticky. I talk about my efforts to write polynomial versions of 6D noise at http://www.gamedev.net/blog/33/entry-2254250-derivative-noise/

The code posted by vinterberg demonstrates the blending method which is the simplest method, mathematically. For basic cloud noise, it's probably suitable. However, for high-contrast functions, or functions with strongly defined patterns, it might result in blending artifacts, as this image shows:

cellular_seamless.jpg

The variant on the left uses the blending scheme. 4 areas of 2D cellular noise are generated, then blended together to create the seamless pattern. At the edges, the pattern achieves a clarity that the center lacks, due to the center being a near-equal blend of 4 different noise sets, while the edges are more strongly weighted toward one of the 4 areas. Expanded out across a grid, the pattern becomes unmistakeable:

1wJ3lJw.png

The one on the right uses 4 dimensional noise and the clifford torus mapping to generate the seamless pattern, and while the curvature of the domain space is apparent in the curvy distortion of the shapes (in standard 2D cellular noise, these shapes are convex polygons with straight edges) the overall contrast of the pattern is preserved throughout the image, as is apparent when the image is tiled:

KcbQyP4.png

Share this post


Link to post
Share on other sites

You can create tileable 2D simplex noise for a particular resolution trivially by using equilateral triangles as the 2D simplex. If you align the triangles to a given resolution you just need to align/tile/wrap the grid points and it just works. 

Share this post


Link to post
Share on other sites

The best way I have found of making sure there are no seems in noise textures is trivial.

 

You change the input to the noise function to something that matches the shape you want to map the noise onto.

 

The simplest case is a sphere.

 

Instead of calculating the noise function from a 3d position, I simply swapped to polar coordinates. This gave me a smooth 3D noise with no seems.

 

It also made terrain generation simple as I could use the latitude / longitude of any position as the input to my noise function and it would produce the same result every time.

 

So maps became easy, Normal calculation trivial. Everything just fell into place.

 

You can see what I mean here

 

http://stainlessbeer.weebly.com/planets-1-mercators.html

Edited by Stainless

Share this post


Link to post
Share on other sites

Sorry for the late response but I was busy with holidays :D

First of all I tried the blending method, and it looked awful.

[attachment=31985:clouds_blended.png]

So I tried the method with the higher dimensional torus as planned. For that I used the 6D Simplex Noise of the Accidental Noise Library.

Well, the results look good enough but somhow I get some strange artifacts, regular lines in an 45° angle. I suppose the noise method is responsible for that but I can't work out how, since the artifacts are hard to see. I attached four Screenshots which fairly show my problem.

I hope someone has an idea what exactly causes this problem. If you wish to see some code, just say so.

[attachment=31981:clouds_artifacts1.png][attachment=31982:clouds_artifacts2.png][attachment=31983:clouds_artifacts3.png][attachment=31984:clouds_artifacts4.png]

Share this post


Link to post
Share on other sites
The issue with the lines is a problem inherent to the calculation of the weighting factors in the 6d implementation of simplex noise. It's been several years now, so I can no longer remember the exact details of why I was unable to find weight factors that would work correctly, but the ultimate conclusion I came to was that using standard perlin noise gave much better results than simplex at higher dimensions.

Standard noise is, for me at least, easier to extend. Simplex noise is simple enough conceptually, but the implementation gets very weird in a hurry as you add dimensions. I based my simplex generator on a python n_dimensional generator I found somewhere. The python generator exhibited the same anomalous behavior.

I would say to give standard noise a try to see if results work better for you. Meantime, I might dig back into things to see if I can work out the underlying problem this time around.

Share this post


Link to post
Share on other sites
Think I may have found a quick (not 100% correct) fix for the lines in the simplex noise. If you are using the latest code from the repo at Github (link in signature), then the fix has been pushed. If you are using older code (and your earlier link to sourceforge leads me to believe that such is the case) then you'll have to make the fix yourself. The fix is: open the noise_gen.cpp file. Find the function simplex_noise6D. Scroll through the function until you find the line that reads n+=gr*t*t*t*t; (it'll be around line 1224) and change it to n+=gr*t*t*t*t*t; Rebuild, and it should be a bit better. It seems to be better in my personal tests at least:

http://imgur.com/a/tIviS

If you still get lines, add another *t to the expression.

The lines occur because the weights for the simplex corners are being overscaled. While adding a couple points to the exponent of gr*t^4 scales the weight smaller to help correct that, it also alters the overall character of the result. It'll look a lot less sharp the higher you go.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!