Perlin noise constructor and method(s) use

Hi to all,
Panda3d is really an amazing engine, I didn’t know it supported both 2d and 3d Perlin noise generation! That said, documentation regarding this is rather scant; other than the api reference and a few topics here and there on the forum, there really is nothing else to help someone tap into these cool features.
So, here are my questions, to anyone that’s well versed in using them:

  • For both PerlinNoise2 and PerlinNoise3 classes, there are 3 constructor versions, for the third constructor version, you can pass some parameters:
PerlinNoise2(sx, sy, table_size, seed)
PerlinNoise3(sx, sy, sz, table_size, seed)

Is there a range for sx, sy and sz? A minimum and maximum value they can be, such as, they must be a value between 0 and 1? For table_size, must a value of 256 always be assigned to it? And for the seed, it can be any unsigned long int, right?

  • Then, for these methods:
.noise(double x, double y) /.noise(double x, double y, double z)  
.setScale(double sx, double sy)/.setScale(double sx, double sy, double sz)

Are there set minimum and maximum values that can be passed to them as well? What is the range of values that can be passed? Must they be between 0 and 1?

That’s all. I appreciate any help in advance as always.

I don’t have direct answers for you, but I did recently use the Perlin Noise functionality to generate some images, and perhaps the code involved will give you some reference points:

Note that it’s designed to create 1024 x 1024 x 1024 3D noise, and writes the resultant images out to a set of files, and so it can take a long time to run! If you want to simply test it to see what sort of output it produces, I recommend setting the value of “numLayers” to 1.

from panda3d.core import PerlinNoise3, PNMImage, Filename

import datetime

print ("Generating noise...")

# Panda's Perlin Noise class doesn't seem to handle multiple noise-levels
#  by itself, so we're generating each level individually, and combining them
#  ourselves.
noise = PerlinNoise3(100, 100, 10, 1024, datetime.datetime.now().toordinal())
noise2 = PerlinNoise3(70, 70, 5, 1024, datetime.datetime.now().toordinal())
noise3 = PerlinNoise3(40, 40, 2, 1024, datetime.datetime.now().toordinal())
noise4 = PerlinNoise3(10, 10, 0.2, 1024, datetime.datetime.now().toordinal())

# A PNMImage into which to write our noise
image = PNMImage(1024, 1024, 3)
# The number of layers--i.e. images--to generate and save
numLayers = 1024

# For each layer/image, generate noise at each level and combine it all,
#  then write the result to file.
for layer in range(numLayers):
    for i in range(1024):
        for j in range(1024):
            # Note that each layer is scaled and offset to adjust its prominence
            #  in the final image
            noiseVal = (noise.noise(i, j, layer) + 1 + noise2.noise(i, j, layer)*0.5 + 0.5 + noise3.noise(i, j, layer)*0.3 + 0.3 + noise4.noise(i, j, layer)*0.1 + 0.1)/2.9

            # Write the pixel to the image
            image.setXel(i, j, noiseVal)

    # Determine the file-name, with leading zeroes.
    layerStr = ""
    if layer < 10:
        layerStr += "000"
    elif layer < 100:
        layerStr += "00"
    elif layer < 1000:
        layerStr += "0"
    layerStr += str(layer)

    # Write the image!
    image.write(Filename("images/noise_" + layerStr + ".png"))

print ("Done!")

Okay, that does answer some questions but raises others, so the sx,sy and sz values do not have to be a value between 0 and 1. Similarly, the table size need not be constrained to 256 as you set it to 1024.
So the values sx,sy and sz that you pass to the constructor, those set the initial noise scale/frequency, right? Higher values mean greater frequency and lesser values mean lesser frequency?
Then, why did you use a value of 1024 for the table_size? Does that set the extent of the noise to 1024? Like from 0-1023, so that the minimum value you could input is 0 and the maximum is 1023, when sampling the noise at a point (x,y,z)? So that, given a table_size of 1024, it would not be possible to sample noise at point (2048,2048,2048)?
Sorry for bombarding you this way…

You’re not bombarding me at all. :slight_smile:

That said, for the most part I really don’t know the answers, I’m afraid: as best I recall, I came to those values by experimentation. As you say, this feature doesn’t seem to be much documented. :/

I do think that you have the effects of “sx”, “sy”, and “sz” in the initial constructor the wrong way around: I think that higher values increase wavelength (and thus reduce frequency), while lower values decrease wavelength (and thus increase frequency). That is, those values control the size of the “spots” in the noise, with higher values producing larger “spots”

Alright, thanks for clarifying that, though in my defense, it was posed as a question! :grimacing: So larger values mean longer wavelengths which of course in turn mean lesser frequencies which means larger spots produced by the noise and the opposite is true for smaller values.
Thank you, I just hope that the rest is also answered and this obscurity tapers.

Fair enough! And perhaps one of the devs, or another user, might chime in with more information. :slight_smile:

The Perlin Noise implemented in Panda is based on the original Perlin gradient noise, it’s input is scaled and repeatable. The noise is a gradient noise, based on a permutation table (which size by default is 256 in Panda). When you want to get a noise value for a given point, the algo will find the integer points around your point, calculate a gradient for each corner of the square using the permutation table and interpolate the result with the decimal part of your point.

So, to have some random noise, and not just a smooth gradient, yours points must not be too close, so so if you have a permutation table of size 256, you should not use point with less than 1/256 (well actually it depends on what you want :stuck_out_tongue: )

To help you, Panda perform a scaling of your point before generating the noise, so :

PerlinNoise2(1, 1, 256).noise(128, 128)

is equivalent to

PerlinNoise2(256, 256, 256).noise(0.5, 0.5)

As the noise function is repeatable, there is no constraint of the noise position, you can use whatever you want. Also, as the scaling is a simple division, there is no limit either (except 0 for obvious reasons)

As Thaumaturge said, (sx, sy, …) can be seen as a scale of the noise frequency, a larger value meaning a higher frequency and so more variation per unit.

finally, you usually don’t need to change table size, 256 is a good compromise, anyway Perlin noise is quite boring used alone, instead you combine it (or stack it as defined in Panda) to make FBM noise.

You say that the function is repeatable, but I never did find a way to get Panda to generate noise with tiling edges. Is there a way to do that?

Does that mean that there’s a built-in way to combine levels of noise? As you can see above, I did it “manually”, but it would be interesting to know whether Panda does in fact have a means of handling it internally!

This is where the table size or the scaling values become most relevant, the tiling or repetitiveness happens when you wrap over the permutation table, so with scale = 1, your noise point must go from 0 to 255.

Yes, you can use StackedPerlinNoise (http://www.panda3d.org/reference/cxx/classStackedPerlinNoise3.html) which do all the dirty work for you :slight_smile: You can specify the number of perlin noise to stack, the freqency gain and the amplitude dampening.

So, with a size of 100 (as in the code that I posted above), the points would only wrap at 25 500? Interesting–that would explain why I was having trouble, indeed.

Ah, very neat! I missed that class entirely!

Thank you for your answers. :slight_smile:

Great! Thanks for clarifying a few things up. Though in terms of the values passed to the constructor, Thaumaturge stated the inverse to what you just said, so:

Actually contradicts what he said, that is, larger values passed mean larger wavelengths, which mean lower frequency, which in turn would mean less variation per unit. So which is which?

Other than that: table size by default is 256 but does it have a maximum limit to what it can be set to?
Values passed to noise and setScale can be any value, understood.

Scale and frequency are the two faces of the same coin :slight_smile: For PerlinNoise here, the input point is divided by the scale factor, so a larger scale means a higher noise frequency. But in doubt, just test it, the result is usually pretty obvious :stuck_out_tongue:

There is no limit (though it’s not really useful to use too big a table, the noise is uniform), but it must be a power of 2 !

Alright, though one tiny little detail has arisen and caught my eye, when you say:

What do you mean by “your noise point must go from 0 to 255”? What is a noise point in this context? Is it whatever is returned by the noise function, i.e. noisePoint=perlinNoiseObject.noise(x,y)? Or did I miss the mark on that?

I mean the 2D or 3D point for which a noise value is calculated, i.e. the parameter of the noise() method. And to have tiling, the texture size your are building (if you are using perlin noise to generate a texture) must be a multiple of the table size time the scale.

1 Like

Nice, that indeed has completely cleared up my questions regarding this matter. You both have my sincerest gratitude!:smile:

1 Like