# Perlin Noise: Lines at Integer Boundaries?

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

## Recommended Posts

Is it normal to see faint lines in two-dimensional perlin noise with a frequency > 1.0? The screenshot below is a noise texture generated with a frequency of 4.0 (only one octave). The faint divisions of the image into 4x4 cells make me wonder if my algorithm is flawed, or if I'm simply seeing 0 values at integer boundaries. Thoughts? I'll post source code if necessary.

##### Share on other sites
hmm, its been a while since i've worked with the noise function, but questions of behavior depend a lot on your method of generation. source code will help.

as a first guess, what function are you using to interpolate? you could be looking at just second derivative discontinuities.

##### Share on other sites
It looks like a bug; I had something similar in one of the first terrain engines I ever wrote.

I don't remember what the bug was exactly, it was a while ago. However, I vaguely recall it being something silly like a misplaced bracket in a calculation.

##### Share on other sites
Quote:
 Original post by doctorsixstringtwo-dimensional perlin noise [...] (only one octave)

Are you sure you're talking about the real Perlin Noise?

##### Share on other sites
My first version was a direct port from Perlin's original C source to Python. My second version was refactored quite a bit to be more Pythonic and easier to read (based on article). I have not done any profiling or optimization yet.

I've posted the code below, which I'm hoping shouldn't be too hard to follow. Basically, I have a Noise2 class with a next() method for generating a noise value. I've also created a series() method for generating a 2D array of noise values from next(). I'm using linear interpolation.

Noise2 class
import math, randomdef s_curve( t ):	""" 3*t^2 - 2*t^3 """	return t * t * (3.0 - 2.0 * t)def lerp( t, a, b ):	return a + t * (b - a)class Noise2(object):	def __init__( self, interval=256, seed=None ):		""" Initializes an 2-dimensional noise generator.			interval:	number of entries in the lookup table.  The noise will repeat at this interval.			seed:		Random number generator seed.  Use the same value to generate identical graphs.		"""		self.interval = interval		self.rnd = random.Random(seed)		self.p = [ i for i in xrange(0, interval) ]		self.g = [ self.generateRandomUnitVector() for i in xrange(0, interval) ]		self.rnd.shuffle( self.p )	def generateRandomUnitVector( self ):		v = [ self.rnd.uniform( -1.0, 1.0 ), self.rnd.uniform( -1.0, 1.0 ) ]		length = math.sqrt(v[0] * v[0] + v[1] * v[1])		v[0] /= length		v[1] /= length		return v				def next( self, x, y, amplitude=1.0 ):				# compute the integer position of the four surrounding points		x0, y0 = int(x), int(y)		x1, y1 = x0 + 1, y0 + 1		# permutate values to get indices to use with the gradient look-up tables		q00 = self.p[ (y0 + self.p[x0 % self.interval]) % self.interval ]		q01 = self.p[ (y1 + self.p[x0 % self.interval]) % self.interval ]		q10 = self.p[ (y0 + self.p[x1 % self.interval]) % self.interval ]		q11 = self.p[ (y1 + self.p[x1 % self.interval]) % self.interval ]		# Compute vectors from the four points to the input point		tx0 = x - int(x)		tx1 = tx0 - 1		ty0 = y - int(y)		ty1 = ty0 - 1		# Compute the dot-product between the vectors and the gradients		v00 = (tx0 * self.g[q00][0] + ty0 * self.g[q00][1]) * amplitude		v10 = (tx1 * self.g[q10][0] + ty0 * self.g[q10][1]) * amplitude		v01 = (tx0 * self.g[q01][0] + ty1 * self.g[q01][1]) * amplitude		v11 = (tx1 * self.g[q11][0] + ty1 * self.g[q11][1]) * amplitude		# interpolate to get the final value		wx = s_curve(tx0)		wy = s_curve(ty0)		a = lerp( wx, v00, v10 )		b = lerp( wx, v01, v11 )		return lerp( wy, a, b )	def series( self, width, height, frequencyMod=1.0, amplitudeMod=1.0, octaves=8, 		octaveFunc=lambda octave: (pow(2.0, octave), 1.0/pow(2.0, octave)) ):		""" Returns a complete series of accumulated perlin noise.					size:			Size of result series			frequencyMod	Frequency modifier			amplitudeMod	Amplitude modifier			octaves:		Number of "layers" of noise, each having a custom frequency and amplitude			octaveFunc:		Optional method that returns the frequency and amplitude for a specified octave.		"""				result =  [ [ 0.0 for i in xrange(0, height) ] for i in xrange(0, width) ]				for octave in xrange(0, octaves):					frequency, amplitude = octaveFunc(octave)			frequency *= frequencyMod			amplitude *= amplitudeMod			for x in xrange(0, width):				fx = float(x) / width * frequency			# calculate x value between 0.0 and 1.0, then multiplied by frequency				for y in xrange(0, height):					fy = float(y) / height * frequency		# calculate y value between 0.0 and 1.0, then multiplied by frequency					n = self.next( fx, fy, amplitude )					result[x][y] += n						# accumulate noise value						return result

Example usage
noise = Noise2().series( width, height, 4.0, 1.0, 1)

##### Share on other sites
Quote:
 My first version was a direct port from Perlin's original C source to Python.

Perlin's first version, if I recall correctly, had second-order discontinuities, which would result in what you are seeing. He later improved his noise to remove that problem.

EDIT: Here it is: http://mrl.nyu.edu/~perlin/paper445.pdf

##### Share on other sites
I think the problem might have more to do with the other problem addressed by Perlin in that same paper, dealing with the distribution of the random vectors. The generateRandomUnitVector is biased towards diagonal vectors, since it is generating vectors in a unit square and then normalizing them. To get a uniform distribution you can generate a random angle and then use sine and cosine (or use the current method but reject vectors with a length greater than 1 to only use normalized vectors that were from inside the unit circle, or there are even other methods like generating two normally distributed numbers and then normalizing them).

##### Share on other sites
Great, thanks guys. You can actually see the faint divisions in the 1a and 2a images in Perlin's paper. I will take a closer look at the PDF tonight and post my changes.

##### Share on other sites
Perfect! Thanks for the tips, guys.

I changed my s-curve function to this...

def s_curve( t ):	""" 6*t^5 - 15*t^4 + 10*t^3 """	return t * t * t * (6.0*t*t - 15.0*t + 10.0)

...and I changed the generateRandomUnitVector method in my Noise2 class to this...

	def generateRandomUnitVector( self ):		angle = self.rnd.uniform( 0, math.pi * 2.0 )		return [ math.sin(angle), math.cos(angle) ]

Here's some results: