Archived

This topic is now archived and is closed to further replies.

griffenjam

Perlin Noise takes too long?

Recommended Posts

quote:
Original post by griffenjam
Right now I am using perlin noise to generate clouds but it takes forever to generate them, is this common or am I doing something wrong?

Do you generate your noise everytime your app starts?


how long?

i generate a 1024x1024 texuture with exponential clouds + alpha in aboug 1.5s with a rip of Ken Perlins original code. at the loading i generate 7 1024x1024 texutres for my clouds for different weathers and i interpolate them.

post your code here so we can see where is the bottleneck.

Share this post


Link to post
Share on other sites
This is my perlin noise code.



float CPerlin_Parent::Noise2d(int x, int y)
{
long int n = x + y * 57;
n = (n<<13) ^ n;
float ret = (float)( 1.0 - ( (n * (n * n * 15731 + 789221)
+ 1376312589) & 0x7fffffff ) / 1073741824.0);
return ret;
}

float CPerlin_Parent::SmoothNoise2d(int x, int y)
{
float corners = ( Noise2d(x-1, y-1) + Noise2d(x+1, y-1) + Noise2d(x-1, y+1) + Noise2d(x+1, y+1) ) / 16.0f;
float sides = ( Noise2d(x-1, y) +Noise2d(x+1, y) + Noise2d(x, y-1) + Noise2d(x, y+1) ) / 8.0f;
float center = Noise2d(x, y) / 4.0f;
return corners + sides + center;
}

float CPerlin_Parent::Interpolate(float x, float y, float a)
{
float fac1 = (float) (3*pow((1-a), (double)2) - 2*pow((1-a) ,(double)3));
float fac2 = (float) (3*pow(a,2) - 2*pow(a,3));

return x*fac1 + y*fac2; //add the weighted factors
}

float CPerlin_Parent::GetValue(float x, float y)
{
int Xint = (int)x;
int Yint = (int)y;
float Xfrac = x - Xint;
float Yfrac = y - Yint;

float x0y0 = SmoothNoise2d(Xint, Yint); //find the noise values of the four corners
float x1y0 = SmoothNoise2d(Xint+1, Yint);
float x0y1 = SmoothNoise2d(Xint, Yint+1);
float x1y1 = SmoothNoise2d(Xint+1, Yint+1);

//interpolate between those values according to the x and y fractions
float v1 = Interpolate(x0y0, x1y0, Xfrac); //interpolate in x direction (y)
float v2 = Interpolate(x0y1, x1y1, Xfrac); //interpolate in x direction (y+1)
float fin = Interpolate(v1, v2, Yfrac); //interpolate in y direction

return fin;
}

float CPerlin_Parent::Cosine_Interpolation(float a, float b, float x)
{
double ft = (x+1) * 3.1415927;
double f = (1 - cos(ft)) * 0.5;

return (float)a*(1-f) + (float)b*f;
}


float CPerlin_Parent::Cosine_Interpolate2D(float x, float y, int i)
{
int integer_X = (int)x;
float fractional_X = x - integer_X;

int integer_Y = (int)y;
float fractional_Y = y - integer_Y;

float v1 = SmoothNoise2d(integer_X, integer_Y);
float v2 = SmoothNoise2d(integer_X + 1, integer_Y);
float v3 = SmoothNoise2d(integer_X, integer_Y + 1);
float v4 = SmoothNoise2d(integer_X + 1, integer_Y + 1);

float i1 = Cosine_Interpolation(v1 , v2 , fractional_X);
float i2 = Cosine_Interpolation(v3 , v4 , fractional_X);

return Cosine_Interpolation(i1 , i2 , fractional_Y);
}

float CPerlin_Parent:erlinNoise2D(float x, float y)
{
float total = 0;
fFreq = 1;
float fPersistance = 1.0f; //The "weight" each octave will have

for(int i=0; i {
total += GetValue((x/64.0f)*fFreq, (y/64.0f)*fFreq) * fPersistance;
fFreq *= 2;
fPersistance *= fPersistanceMod;
}

// total = total*0.5 + 0.5;
if(total < 0)
total = -total;
if(total > 1.0f)
total = 1.0f;

return total;

}



I would post my cloud code as well, but everything thaty uses perlin noise takes forever*, including landscape.

*forever is 5 minutes for a 512x512 texture.

Share this post


Link to post
Share on other sites
You need to try a different approach...

Instead of using the perlinnoise function that does all the smoothing and everything, just use your basic noise function.

That will give you a texture that looks like static on a tv screen. Then render this to a larger texture using bilinear filtering. This will smooth it out faster for you and you can still get the same effect by starting with a small noise texture(16x16 or 32x32) and rendering it to your final sized texture(1024x1024 prob.). Then move up a size and do the same. Then combine them all.


Like this:

1)

16x16 render to 1024 w/ bilinear filtering
32x32 render to 1024 w/ bilinear filtering
64x64 render to 1024 w/ bilinear filtering
128x128 render to 1024 w/ bilinear filtering
256x256 render to 1024 w/ bilinear filtering
512x512 render to 1024 w/ bilinear filtering


2)
combine all textures

3)
subtract a value using the texturestagestate d3dtop_subtract (ibelieve) and subtract a gray texture from it in order to get more clear areas.

Share this post


Link to post
Share on other sites
Perlin Noise can get a little expensive, especially since you're doing lots of dividing, float-int conversion, and sampling. The usual optimizations are to use fixed point and use the old scaling-the-number-range tricks to get rid of the divides in the inner loops, that way you're working strictly with integers and without dividing a whole lot.

Or do it using the method above with hardware. There was a thread about this a while back that I posted in (did you start it?) about HW PN.

------------
- outRider -

[edited by - outRider on November 17, 2003 12:22:31 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Some improvement techniques :

1/ as Eelco said, inline some functions : Noise2d() and Interpolate() are good candidates.

2/ do not use "double". When you write "1.0" or "1073741824.0" your *are* using double. The compiler will not optimize this. Instead of this you can use "1.0f" and "1073741824.0f" - these are floats (32 bits). They are *a lot* faster.

3/ starting with VC.NET a lot of floating point functions are SSE-enabled (I d not have the list here ; at least I''m sure it''s in the last MSDN library chm files). Instead of using "pow()" or "cos()" you''ll have to use "powf()" and "cosf()".

4/ use your own "ftol()" function : when you transform an float value to an integer value, the compiler implicitely calls "ftol()". This function is precise but slow. You can get nearly the same results by using a small inlined function (don''t have it here : I''m at home )

5/ the pow() function is a no-no. Use a look up table.

6/ you can do the same for the cos() function if you want.

This should speed up your implementation.

Another solution is to take the basic Ken Perlin code and to tweak it until you get decent results. You should be able to do a 512x512 texture in less than 500 ms without any kind of super-genious_optimization.

A third solution, as Chaucer proposed, is to use multiple simple noise layers (this is what you do with your loop). I believe that clanlib as a noise function which work like that (very fast).

Hope it helps,

--
Emmanuel Deloget

Share this post


Link to post
Share on other sites
You don''t need a look-up table or pow() when you''re doing x^2 or x^3. Just use (x*x) or (x*x*x), it''s probably faster than a table look-up. I''d also try using a non-smoothed noise function and linear interpolation and see if the quality is good enough for your application.

Share this post


Link to post
Share on other sites