Sign in to follow this  

blocky Perlin noise (*PICS INCLUDED*)

This topic is 4724 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

Ok, they say a picture is worth a thousand words. Here is a picture of my generated perlin noise. This is just 1 octave. I would think it has something to do with my smoothing code
template <class SmoothPolicy>
struct LinearInterpolation2DPolicy : public SmoothPolicy
{
	inline float Smooth (float x, float y)
	{
		int integer_x    = int(x);
		float fractional_x = x - integer_x;

		int integer_y    = int(y);
		float fractional_y = y - integer_y;

		float v1 = SmoothNoise(integer_x,     integer_y);
		float v2 = SmoothNoise(integer_x + 1, integer_y);
		float v3 = SmoothNoise(integer_x,     integer_y + 1);
		float v4 = SmoothNoise(integer_x + 1, integer_y + 1);

		float i1 = Interpolate(v1 , v2 , fractional_x);
		float i2 = Interpolate(v3 , v4 , fractional_x);

		return     Interpolate(i1 , i2 , fractional_y);

	}

	inline float Interpolate(float a, float b, float x)                  //fastest
	{ 
		return  a*(1-x) + b*x;
	}

};

template <int seed1, int seed2, int seed3>
struct DefaultNoisePolicy2D
{
	inline float Noise (float x, float y)  // [-1.0, 1.0]
	{
		 int n = x + y * 57;
		 n = (n<<13) ^ n;
		 return ( 1.0 - ( (n * (n * n * seed1 + seed2) + seed3) & 0x7fffffff) / 1073741824.0);
	}
};


template <class NoisePolicy>
struct DefaultSmoothPolicy2D : public NoisePolicy
{
	inline float SmoothNoise(float x, float y)
	{
		float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16.0f;
		float sides   = ( Noise(x-1, y)  +Noise(x+1, y)  +Noise(x, y-1)  +Noise(x, y+1) ) /  8.0f;
		float center  =   Noise(x, y) / 4.0f;

		return corners + sides + center;
	}
};



template <class NoisePolicy,template<class T> class SmoothPolicy, template<class T>class InterpolationPolicy, int Octaves, class FilterPolicy=NullFilterPolicy>
class Perlin : public InterpolationPolicy<SmoothPolicy<NoisePolicy> >, public SmoothPolicy<NoisePolicy>, public NoisePolicy, public FilterPolicy
{
	bool TableBuilt;               //Table lookup shit
	float Frequency [Octaves];
	float Amplitude [Octaves];

public:
	Perlin() {TableBuilt = false;}

	void BuildLookupTable(float Persistence)
	{

		for (int i = 0; i < Octaves; i++)
		{
			Frequency[i] = powf(2.0f,(int)i+1);
			Amplitude[i] = powf(Persistence,(int)i+1);
		}
	}

	float Generate (float x, float y)
	{
		float total = 0;

		for (int i = 0; i < Octaves; i++)
		{
			total = total + (Smooth(x * Frequency[i], y * Frequency[i]) * Amplitude[i]);
		}

		return Filter ((total+1)/2.0f);
	}

};

Sorry about the policies, but this library is actually pretty big with plenty of filters and whatnot. Here is a picture of a 2-pass noise from when the library actually worked, the first with a sigmoid filter, the second with just an identity filter. I honestly have absolutely no idea what's going on. I had a working perlin noise lib, and then I did a bunch of things to optimize it (I did triple its speed) and now it produces blocky images. But the images really do resemble the noise. Here is the same seed values for the 2-pass image with the fubar'd noise routines.

Share this post


Link to post
Share on other sites
I remember having the same problem, but it was a long while ago so i don't remember how i fixed it or if i fixed it.

I took a look at your code but i can't find the problem. Anyway, i have code that works, it's quite similar to yours so i hope it helps:



#include "stdafx.h"
#include "perlinnoise.h"
#include "image.h"
#include "vectors.h"

CPerlinNoise::CPerlinNoise()
{
}

float CPerlinNoise::LinearInterpolation(float t, float a, float b)
{
return a * (1.0f - t) + b * t;
}

float CPerlinNoise::SCurve(float a)
{
return a * a * (3.0f - 2.0f * a);
}

float CPerlinNoise::RandomFloat()
{
return (float)( (rand() % (MASK + MASK) - MASK) ) / (float)MASK;
}

void CPerlinNoise::GenerateTables()
{
srand(GetTickCount());

int i;
for (i = 0; i < MASK; i++)
{
permutationTable[i] = i;

randomTable[i].x = RandomFloat();
randomTable[i].y = RandomFloat();
randomTable[i] = Normalize(randomTable[i]);
}

int k, temp;
CVector2 t;
for (i = 0; i < MASK; i++)
{
k = rand() % MASK;

temp = permutationTable[i];
permutationTable[i] = permutationTable[k];
permutationTable[k] = temp;

// t = randomTable[i];
// randomTable[i] = randomTable[k];
// randomTable[k] = t;
}

for (i = MASK; i < MASK + MASK; i++)
{
permutationTable[i] = permutationTable[i - MASK];
randomTable[i] = randomTable[i - MASK];
}
}

void CPerlinNoise::Setup(float x, int &grid1, int &grid2, float &dist1, float &dist2)
{
float t = x + MAXIM;
int it = (int)t;

grid1 = it % MASK;
grid2 = (grid1 + 1) % MASK;
dist1 = t - it;
dist2 = dist1 - 1.0f;
}

float CPerlinNoise::PNoise2D(float x, float y)
{
int Xint0, Xint1, Yint0, Yint1;
float Xfrac0, Xfrac1, Yfrac0, Yfrac1;

Setup(x, Xint0, Xint1, Xfrac0, Xfrac1);
Setup(y, Yint0, Yint1, Yfrac0, Yfrac1);

int index1 = permutationTable[Xint0];
int index2 = permutationTable[Xint1];

int corner1 = permutationTable[index1 + Yint0];
int corner2 = permutationTable[index2 + Yint0];
int corner3 = permutationTable[index1 + Yint1];
int corner4 = permutationTable[index2 + Yint1];

CVector2 v;
float kx, ky, sx, sy;
float a, b;

sx = SCurve(Xfrac0);
sy = SCurve(Yfrac0);

v = randomTable[corner1]; kx = Xfrac0 * v.x + Yfrac0 * v.y;
v = randomTable[corner2]; ky = Xfrac1 * v.x + Yfrac0 * v.y;
a = LinearInterpolation(sx, kx, ky);

v = randomTable[corner3]; kx = Xfrac0 * v.x + Yfrac1 * v.y;
v = randomTable[corner4]; ky = Xfrac1 * v.x + Yfrac1 * v.y;
b = LinearInterpolation(sx, kx, ky);

return LinearInterpolation(sy, a, b);
}


float CPerlinNoise::GenData(float x, float y, float freq, int octaves, float pers)
{
int i;
float f = 0.0f;

float fr = freq;
float amp = 1.0f;

for (i = 0; i < octaves; i++)
{
f += PNoise2D(x * fr, y * fr) * amp;
fr *= 2;
amp *= pers;
}

return f;
}

int CPerlinNoise::Generate(unsigned char *buf, int colors, int size, float freq, int octaves, float pers)
{
float *data;
float min, max, sc, val;
int i, j;
float freq_fact = freq;

GenerateTables();

data = new float [size * size];
if (!data)
{
return SYS_ERR;
}
min = 1.0f; max = -1.0f;

for (i = 0; i < size; i++)
{
for (j = 0; j < size; j++)
{
val = GenData(i, j, freq, octaves, pers);

data[i * size + j] = val;

if (val < min) min = val;
if (val > max) max = val;
}
}

sc = 1.0f / (max - min);

BYTE value;
for (i = 0; i < size * size; i++)
{
buf[i] = (BYTE)(((data[i] - min) * sc) * 255.0f);
}

delete [] data;

return SYS_OK;
}




EDIT: Separated source file from header, removed some bugs uninterseting stuff



#ifndef _TER_GEN_H

#define _TER_GEN_H

#include "vectors.h"

#define MAXIM 4096
#define MASK 256

class CPerlinNoise
{
public:

CPerlinNoise();

int Generate(unsigned char *buf, int colors, int size, float freq, int octaves, float pers);

private:

void Setup(float x, int &grid1, int &grid2, float &dist1, float &dist2);

float SCurve(float a);
float LinearInterpolation(float t, float a, float b);

float PNoise2D(float, float);

float GenData(float x, float y, float freq, int octaves, float pers);

void GenerateTables();
float RandomFloat();

int permutationTable[MASK + MASK];
CVector2 randomTable[MASK + MASK];

};

#endif
[/code]



[Edited by - Ilici on January 2, 2005 4:44:07 AM]

Share this post


Link to post
Share on other sites
I think the interpolation and smoothing part is fine; the math checks out, anyways. Looking at the picture, it seems like the interpolated regions are fine - the real problem is that the corners of each region don't interpolate into the same values.

If I'm reading your templating right, the problem is most likely in the use of the SmoothNoise() function instead of calling Noise() directly from the interpolator, the way standard Perlin works. Classic Perlin takes a pre-generated array of random values and basically runs them through a bilinear (or trilinear) filter. I've been staring at the SmoothNoise() function for about 20 minutes now and I still can't figure out what exactly it is supposed to be doing. The effect is basically to pull random data from all around the sample point and blend it together (I think) - but with floating point error it feels to me like the net effect would be utter chaos. The patterns in your multi-octave sample image suggest this as well - that the problem isn't interpolation, the problem is interpolating between the right numbers.

Incidentally, Ilici's code differs from yours (functionally) in only one major way that I can tell after a quick skim - it doesn't include the insanity in SmoothNoise() in any form. For what it's worth I've never seen that sort of math going on (in that part of the noise pipeline, to be precise) in any working Perlin-style noise implementation, either.

Instead of calling SmoothNoise() from the Smooth() function (eurgh - sorry but your naming setup is extremely confusing [wink]) try just making a straight call to Noise().

Share this post


Link to post
Share on other sites
Well, the smoothnoise function just takes a weighted average so the noise isn't so sharp. And that is why I use that instead of the noise function in my interpolater methods.

Its the same exact setup I had when i generated that one pic that looks fine. I just happened to mess something up and I can't figure it out!

Thanks though!

Share this post


Link to post
Share on other sites
I spotted only one potential bug... all your problems might go away if you'll write

inline float Noise (int x, int y) // [-1.0, 1.0]
{
int n = x + y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * seed1 + seed2) + seed3) & 0x7fffffff) / 1073741824.0);
}

(fonts: bold red is where to change, bold is where change matters)

Another thing, make sure your x and y is positive. Make sure rounding mode is correct.
Tnird things about code, a: one seed is enough, b:i don't think it's really good idea to pass seeds as templated parameters, and c: if you really want some flexibility, pass noise basis function (e.g. perlin, linearly interpolated perlin,etc.) by reference to make it be runtime-changeable.

Share this post


Link to post
Share on other sites
Quote:
Original post by Grizwald
-code-


[wrong stuff]
EDIT: i was wrong, your code looks like Ken Perlin's one from http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

I'll compare yours to his.

1. Try using my random function instead of the one Perlin uses

Share this post


Link to post
Share on other sites
Interestly enough Dmytry, my lib has runtime-changeable noise too, but its not as fast. I have three seed values to achieve the most patterns. About using integers for my method parameters though: I need to use floats. Floats allow me to 'zoom' in on the noise. I'm still not sure how I could achieve the same effects with integers. I also have a policy that fits the outputs to a certain range, as well as the inputs. This allows me to zoom in and out of the noise, translate and rotate it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Grizwald
Interestly enough Dmytry, my lib has runtime-changeable noise too, but its not as fast. I have three seed values to achieve the most patterns. About using integers for my method parameters though: I need to use floats. Floats allow me to 'zoom' in on the noise. I'm still not sure how I could achieve the same effects with integers. I also have a policy that fits the outputs to a certain range, as well as the inputs. This allows me to zoom in and out of the noise, translate and rotate it.

You don't understand how
inline float Noise ( x, y) // [-1.0, 1.0]
{
int n = x + y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * seed1 + seed2) + seed3) & 0x7fffffff) / 1073741824.0);
}
works.
x is converted to integer anyway. y is multiplied by 57 and then converted to integer. I have seen this code somewhere already, really many times. Also, AFAIK seeds have to be prime.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
Quote:
Original post by Grizwald
Interestly enough Dmytry, my lib has runtime-changeable noise too, but its not as fast. I have three seed values to achieve the most patterns. About using integers for my method parameters though: I need to use floats. Floats allow me to 'zoom' in on the noise. I'm still not sure how I could achieve the same effects with integers. I also have a policy that fits the outputs to a certain range, as well as the inputs. This allows me to zoom in and out of the noise, translate and rotate it.

You don't understand how
inline float Noise ( x, y) // [-1.0, 1.0]
{
int n = x + y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * seed1 + seed2) + seed3) & 0x7fffffff) / 1073741824.0);
}
works.
x is converted to integer anyway. y is multiplied by 57 and then converted to integer. I have seen this code somewhere already, really many times. Also, AFAIK seeds have to be prime.


Seeds don't HAVE to be prime, you just get better noise if you do.

Share this post


Link to post
Share on other sites
Quote:

Seeds don't HAVE to be prime, you just get better noise if you do.

Obviously. Therefore if you want better noise (without cleanly-visible patterns (i don't mean broken interpolation)), seeds have to be prime. Of course if you don't care about patterns, seeds can be any.
BTW, Have you tried my suggestion about integers for "inline float Noise "?
Have you checked that in your "Smooth", x and y is >0?

Share this post


Link to post
Share on other sites
Hmm... a few things here.

First:
float sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8.0f;

Why is it +1,-1, why is 1 THE number?

Your system performs calculations:
float xBase1 = something;
float x1 = xBase1 * someFrequency;
float fSeedX1 = x;

float xBase2 = something + 1;
float x2 = xBase2 * someFrequency; // ~=~ x1 + someFrequency;
float fSeedX2 = x2 - 1.0; // ~=~ fSeedX1 + (someFrequency - 1.0);

While fSeedX1 and fSeedX2 should be the same, don't they?
I assume here that you call your Generate function in a loop or something, where x is incremented by one.


Second:

int n = x + y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * seed1 + seed2) + seed3) & 0x7fffffff) / 1073741824.0);

What's that supposed to be?
Wouldn't that noise be symmetric along line x=-57*y ???
Get a real random function, not a "messing in a non-predictible way with bits and what-not". If you use a 1-dim random r(x), you can transform it to 2-dim random like that:

newSeed = seedX *+- r(seedY);
return r(newSeed);

newSeed would still have a patteern, but it would be hidden by the random function. Pattern is a thing, that cannot be avoided, we just make sure we know what's going on with it, not hide it with "random calculations", because random calculations almost never give us random output.


Third:

inline float Interpolate(float a, float b, float x) //fastest
{
return a*(1-x) + b*x;
}

This would be faster:

inline float Interpolate(float a, float b, float x) //fastest
{
return a + (b-a)*x;
}


Keep posting your problems, I'll be watching this thread.
/def

Share this post


Link to post
Share on other sites

This topic is 4724 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this