Sign in to follow this  
SpreeTree

Procedural Texture Generation

Recommended Posts

SpreeTree    396
Hi I'm looking for some good resources regarding procedural generation of textures to map on natural object (for example grass, rock or sand etc.). I'm currently using Perlin noise too generate my landscape but I don't think that will cut it here (especially as I require tile-able images) I know Google exists so I'm hoping some of you can point me to any of your favourite sites on the topic or something off the beaten track. ;) Thanks Spree

Share this post


Link to post
Share on other sites
Tachikoma    575
Texturing & Modeling: A Procedural Approach - For terrains, rocks, and various natural looking textures. Your bible if you're serious about procedural techniques.

Algorithmic Botany - Modeling plants and other biological structures. There is some really cool stuff on there.

An Introduction to Lindenmayer Systems - Basic overview of L-Systems which is a fundamental part of generating plants.

Terraform - Tools to generate terrains.

libnoise - A comprehensive library that allows you to generate all sort of procedural stuff, especially textures and terrains.


p.s. You can make tileable Perlin noise. If the texture (or terrain grid) resolution is 2^n, you can create a mask, m = 2^n - 1, where 0 < m <= 255. You can use m to mask the values returned from the permutation table, thus limiting the noise period. Using the reference Perlin noise as an example:


// Note m = 2^n - 1, where 0 < m <= 255. m must never exceed 255, because the permutation table has only 256 entries.
// Texture resolution is assumed to be 2^n.

static public double noise(double x, double y, double z, int m) {
int X = (int)Math.floor(x) & 255, // FIND UNIT CUBE THAT
Y = (int)Math.floor(y) & 255, // CONTAINS POINT.
Z = (int)Math.floor(z) & 255;
x -= Math.floor(x); // FIND RELATIVE X,Y,Z
y -= Math.floor(y); // OF POINT IN CUBE.
z -= Math.floor(z);
double u = fade(x), // COMPUTE FADE CURVES
v = fade(y), // FOR EACH OF X,Y,Z.
w = fade(z);
int A = (p[X ]+Y) & m, AA = (p[A]+Z) & m, AB = (p[A+1]+Z) & m, // HASH COORDINATES OF
B = (p[X+1]+Y) & m, BA = (p[B]+Z) & m, BB = (p[B+1]+Z) & m; // THE 8 CUBE CORNERS,

return lerp(w, lerp(v, lerp(u, grad((p[AA ]) & m, x , y , z ), // AND ADD
grad((p[BA ]) & m, x-1, y , z )), // BLENDED
lerp(u, grad((p[AB ]) & m, x , y-1, z ), // RESULTS
grad((p[BB ]) & m, x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad((p[AA+1]) & m, x , y , z-1 ), // CORNERS
grad((p[BA+1]) & m, x-1, y , z-1 )), // OF CUBE
lerp(u, grad((p[AB+1]) & m, x , y-1, z-1 ),
grad((p[BB+1]) & m, x-1, y-1, z-1 ))));
}




Now, I haven't compiled nor tested this particular code, so it might be broken. However I used a similar approach for my implementation a while back.

Also note the above code assumes your texture is a square or a cube (if you generate 3D noise). If the texture is rectangular (or a 3D brick), you will need to specify m for each dimension separately.

edit: There some other limitations you need to keep in mind with this approach. For example, if you generate multi-octave Perlin noise, you can only increase the octave sampling frequency by multiples of 2, 4, 8, etc.

[Edited by - Tachikoma on March 17, 2007 9:55:44 PM]

Share this post


Link to post
Share on other sites
SpreeTree    396
Excellent, that was exactly what I was looking for.

The libNoise library looks excellent, and I'll def. look into the book you recommended.

I had seen reference to tile-able Perlin noise, but it wasn;t something I had fully looked into. Using perlin noise like that might be a nice way to generate nice texture blends throughout the terrain.

Thanks again :)

Spree

Share this post


Link to post
Share on other sites
ApochPiQ    23064
If you're interested in reference code, I'd highly recommend downloading the POV-Ray source. The documentation provides a quite detailed description of how the techniques work from a mathematical point of view, and the code is pretty straightforward. As a bonus, you can also visualize the results of various procedural textures directly in POV-Ray.

Share this post


Link to post
Share on other sites
hymerman    221
How would I go about tiling noise from libnoise? Will I need to edit the source to take into account this modification? I can't find anything on tiling libnoise's output elsewhere, but I presume this is a fairly common thing to want to do.

Share this post


Link to post
Share on other sites
mrbastard    1577
noiseutils (semi undocumented part of libnoise) provides a seamless texture making function, but it appears this is done as an image operation, not a part of the procedural basis. Consequently the seamless version loses some of the visual quality of the original - it seems messier and blurrier. This reduction in high frequency detail likely results from a mirror-and-add tiling method.

If the output from that is good enough for your needs then great, otherwise you'll have to change the generators [depressed]

I'm pretty sure noiseutils comes with libnoise. If you have any trouble finding the tiling stuff though, let me know. I've probably still got the my code from when I was playing with it.

Share this post


Link to post
Share on other sites
hymerman    221
EDIT: ignore the stuff below, immediately after writing it I searched the source for 'seam' and found what I was looking for - utils::NoiseMapBuilderPlane::EnableSeamless. Thanks very much for your help :)



I noticed utils::RendererImage::EnableWrap(), and have enabled it, but it doesn't appear to be working. The documentation says:

"This object requires five points (the initial point and its four neighbors) to calculate light shading. If wrapping is enabled, and the initial point is on the edge of the noise map, the appropriate neighbors that lie outside of the noise map will "wrap" to the opposite side(s) of the noise map. Otherwise, the appropriate neighbors are cropped to the edge of the noise map."

I'm not entirely sure what it's referring to though - 5 points of what? Where do I set them?

Have I got the right method, and if so how do I use it?

Thanks for your help, by the way!

Share this post


Link to post
Share on other sites
mrbastard    1577
cool, glad you found it. I remembered it being a bit obscure. Hope it does what you need. Should be OK for noise, but looks crappy for voronoi.

happy to help [smile]

Share this post


Link to post
Share on other sites
rotalever    122
Would libnoise be fast enough to include it in a realtime renderer? I am interested in creating (large) terrains with this library at runtime. If it's slower than a few milliseconds for creating an area of terrain, it would be too slow :-(.
Why realtime? - Well, creating things realtime you do not have to load large amounts from hdd and you only have the currently scene in your RAM.

Share this post


Link to post
Share on other sites
jasjas    262
Quote:
Original post by rotalever
Would libnoise be fast enough to include it in a realtime renderer? I am interested in creating (large) terrains with this library at runtime. If it's slower than a few milliseconds for creating an area of terrain, it would be too slow :-(.
Why realtime? - Well, creating things realtime you do not have to load large amounts from hdd and you only have the currently scene in your RAM.

Hi rotalever,

It would depend on how complex your noise is. Using a single Perlin noise module would be fast enough for real-time generation on a recent PC. By the time you add a bunch of additional noise modules, modifiers, selectors, etc., things would really slow down.

There's a way to almost double the speed of the noise generation. If you compile libnoise yourself, open the noisegen.cpp file, go to the GradientCoherentNoise3D() function and change these lines:


// Create a unit-length cube aligned along an integer boundary. This
// surrounds the input point.
int x0 = (int)floor (x);
int x1 = x0 + 1;
int y0 = (int)floor (y);
int y1 = y0 + 1;
int z0 = (int)floor (z);
int z1 = z0 + 1;


to


// Create a unit-length cube aligned along an integer boundary. This
// surrounds the input point.
int x0 = (x > 0.0? (int)x: (int)x - 1);
int x1 = x0 + 1;
int y0 = (y > 0.0? (int)y: (int)y - 1);
int y1 = y0 + 1;
int z0 = (z > 0.0? (int)z: (int)z - 1);
int z1 = z0 + 1;


For whatever reason, the floor() calls are very slow. Casting to an int and doing a conditional produces the same result, but at a much faster speed.

Currently, I've got this change in the libnoise CVS, but it's not yet in the downloadable build. Once sufficiently tested, this change will be included in the next version.

Quote:
Original post by mrbastard
cool, glad you found it. I remembered it being a bit obscure. Hope it does what you need. Should be OK for noise, but looks crappy for voronoi.

Is there a way to improve the voronoi noise?

-- jas

Share this post


Link to post
Share on other sites
rotalever    122
Quote:
Original post by jasjas
It would depend on how complex your noise is. Using a single Perlin noise module would be fast enough for real-time generation on a recent PC. By the time you add a bunch of additional noise modules, modifiers, selectors, etc., things would really slow down.

Hi,
thanks for fast answer! For my uses I think I need a bit more complex terrain than just a single Perlin noise. Perlin noise itself without some other noise modules and modifiers, etc. would look not very realistic.
Sound like I have to stay with an offline generator and complicated compression techniques to store the huge heightmap (uncompressed size estimated to >4GB).

Regards

Share this post


Link to post
Share on other sites
mrbastard    1577
The voronoi isn't crappy in itself - like the rest of the lib it's pretty sweet [smile].

I found that generating cellular type textures and then making them seamless lost much of the definition, for the reason I mentioned previously. It could be fixed by doing the tiling in the basis function itself - simply wrap around the images edges when calculating distance. Not sure how that would fit into your design though.

Share this post


Link to post
Share on other sites
jasjas    262
Quote:
Original post by mrbastard
The voronoi isn't crappy in itself - like the rest of the lib it's pretty sweet [smile].

I found that generating cellular type textures and then making them seamless lost much of the definition, for the reason I mentioned previously. It could be fixed by doing the tiling in the basis function itself - simply wrap around the images edges when calculating distance. Not sure how that would fit into your design though.

Hi mrbastard,

OK, I see what you're getting at.

You're right, the seamless tiling is done at the image level, not the noise level. That's why the edges look kind of blurry.

If I were to add the capability to do the tiling within the basis noise functions, there would need to be some restrictions on what noise modules, parameters, etc., could be used. For example:
  • In all generator modules (Perlin, Voronoi, etc.), you can only use a frequency equal to a power of 2.

  • In all noise modules, you can only use a lacunarity equal to 2.

  • If using the scale module, you can only scale by a power of 2.

  • If using the rotation module, you can only rotate by multiples of 90 degrees.

I'm not sure if you can use the turbulence module. Maybe if the frequency is a power of 2.

Maybe I should put this in at some point.

-- jas

Share this post


Link to post
Share on other sites
mrbastard    1577
Hi

TBH I don't think it's much of an issue with most of the generators - as long as noise looks noisey and is repeatable, it's not a problem. I only found it a problem with the voronoi module because there's a certain well defined 'cellular' look that I was after. That's why I was talking in terms of distance - I hadn't considered the other modules.

I don't have a deep enough understanding of the maths to comment really, but those restrictions would break one of the best features of your lib - that modules can be arbitrarily combined.

Maybe just add a bool property to the voronoi module controlling whether distance is looped? Hacky, but would do the job.

Share this post


Link to post
Share on other sites
PolyVox    712
Quote:
Original post by jasjas

There's a way to almost double the speed of the noise generation. If you compile libnoise yourself, open the noisegen.cpp file, go to the GradientCoherentNoise3D() function and change these lines:

*** Source Snippet Removed ***
to

*** Source Snippet Removed ***
For whatever reason, the floor() calls are very slow. Casting to an int and doing a conditional produces the same result, but at a much faster speed.

Currently, I've got this change in the libnoise CVS, but it's not yet in the downloadable build. Once sufficiently tested, this change will be included in the next version.


This double the speed?! I'm a little surprised that the algorithm spends so much of it's time doing something as trivial as rounding floats to ints. However, if this really is a bottleneck maybe you can improve it by not doing the 'floor()' at all? Just cast the float to the int directly - it will automatically lose the decimal part.

The only catch is if you need it to work for negative numbers. But then I think there may be some clever bitwise operations you can use - ask me if it comes to this. Basically lots of conditional statements are bad for fast code.

Share this post


Link to post
Share on other sites
Lutz    462
There's a way to get tiling textures without the need to change the interiors of noiselib and without losing the possibility to combine noise modules. The trick is to think 3D!

Instead of a rectangular region, say 0<x<1, 0<y<1, z=0, you sample a TORUS. The coordinates on a torus (donut) are described by two angles, say u and v. Both angles run from 0 to 2*PI. If you sample an image for 0<u<2*PI and 0<v<2*PI, you'll get a tiling texture! See Wikipedia for a mathematical torus description.

All you actually have to do is to choose two radii R and r (radius of outer and inner circles), compute the 3D coordinates according to
x(u, v) = (R + r cos(v)) cos(u)
y(u, v) = (R + r cos(v)) sin(u)
z(u, v) = r sin(v)

and plug them into your noise function.

If you choose r smaller than R, you will notice a stretching of the texture image. If you choose r equal to R, which would result in a square texture, you will notice huge distortions along some side. You can get rid of them by taking r=R/N for some number N (e.g. 2 or 4), sampling the noise function N times for (u/N + 2*PI*n/N,v), n=0,..,N-1 and adding the results. This works for Perlin at least. I'm not sure whether it works for Voronoi, too.

Lutz

Share this post


Link to post
Share on other sites

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