So, once upon a time i wrote a quick and dirty tutorial on procedural texture generation over at
CGEmpire (direct link is
here) and i since we now have our own tutorials forum here i figured i post it.. it's basically a cut'n'paste of the original with a few minor formatting changes.. Enjoy

-----------------------------------------------------------------------------------------------------------------------------------------------
Hi everybody.
I thought I'd write up a quick and dirty tutorial on procedural texture generation.. and here it is.

I'm writing this on the fly, so dont expect the pseudo to work lol(and considering im drunk as ***** lots of errors will be present)

ok, the basics of a good texture generator:
1) WORK ON LAYERS!! I CANT STRESS THIS ENOUGH!!! Work on layers.. dont work on RGB images.. trust me, working on layers will net you the best result in the end. (and what i mean is, work on a single grayscale image for each channel(R,G,B) instead of doing your r=r<<16 | g=g<<8 b=b etc)
2) DONT OVERDO IT! Personally, i have over one hundred generators/filters/effects.. you DO NOT NEED THAT MUCH! i wrote em for completion only.. When my tgen is in its final stage i will remove ALOT and optimize it for size/speed..
3) THINK PROCEDURALLY.. meaning, if you can do something with 20 diffrent ops, you can most likely do it with 5, if you THINK about it enough..
4) WORK ALOT on your file format and load/save routines.. this is where you save space in your end product... for example, i wrote a chladni volume generator (for reference:
Chladni plate interference surfaces ) and with my "current" file format i was at 20 bytes/texture, which is WAY too much for say, a 1k intro.. so with some optimizing i managed to get it down to 8 bytes/texture.. moral of the story, THINK about what you are doing, and WORK hard to optimize for size)
5) LAST BUT NOT LEAST! make sure your filters/generators/color ops/whatever TILE!!! I CANNOT STRESS THIS ENOUGH!! this is what makes or breaks your texture generator.. the best way to do this is to work on texture spaces with a power of 2.. 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 pixels wide/high etc.. and to use the AND operator.. for example, say x1 is 200 and x2 is 80.. instead of using "x1+x2"(which winds up at Y=Y and X=0 if you check for overflows and correct them above 255 for example, which will make your texture look like complete shit.. Use "x1 + x2 and 255" instead, and wind up with Y=Y+1 and X=0 which is an exact tile..
Those are the most important points to think about..
Ok, so lets get started on the yummy stuff

first of all, 90% of the code will be pseudo, or when im lazy delphi/pascal code.. since im writing this with no reference to my code and whatnot

lets get started on generators..
ok, lets assume, that X and Y are always 0-max(X or Y)
Generators Part [1]
the most basic(and imo, one of the most useful ones, the RAND() texture)
for each X
for each Y
{
texture[x+y << 8]=rand(255)
}
now, this is pretty useless by itself, but imagine doing this with say a 3-4 pass gausiann blur or a directional blur(covered later).. yeah thats right, you will have a wicked base for a map distort

(speaking of which, i recently thought about using directional blur to generate good looking fur textures based on wez's "photoshop fur" tutorial

)
Generators Part [2]
The XOR texture.. as easy as it gets.. loop thru your texture and assign it "X xor Y" as the color value. like above but:
texture[x+y >> 8] = x xor y;
extremely useless but still covered since it is a way for newbs to start on their texture generators(hell the xor texture was my first seamless texture generated

)
Generators Part [3]
The Sineplasma texture.. not too hard really.. loop thru x and y again, this time assigning a sin/cos to your final destination pixels.. something like this:
for x=0 to maxX
for y=0 to maxY
{
x2=63.5*( sin( (x_ofs+x1) * ((pi / 128)*x_sines)) );
y2=63.5*( cos( (y_ofs+y1) * ((pi / 128)*y_sines)) );
color=127+round(x2+y2);
texture[x+y <<l 8]=color;
}
ok, my variables, X-Y_Ofs are the phase of the sine(where it starts) and X- Y_sines is the amplitude which defines the number of sines used.
Generators Part [4]
this is a generator you might find extremely useless or extremely useful.. the gradient sphere.. remember your old math classes involving pythagoras? well, if you dont, go back to it and read up some, or just follow the instructions..

for x=0 to maxX
for y=0 to maxY
{
x2=(x-x_ofs)/(size/2);
y2=(y-y_ofs)/(size/2);
color=sqrt(x2*x2 + y2*y2) *256;
Texture[x1+y1 << 8]=color;
}
Generators Part [5]
basically one of the oldskool cloud plasmas, only it's done in one pass.. basically you generate your points at n^X where X is a power of 2, and you interpolate the points inbetween using either a simple cosine or a more advanced spline interpolation.. so you end up with a subplasma type of image.. now to make a "normal" cloud texture of it, you generate a few different ones(the higher the amplitude(brightness) the lower the phase(distance between points) and just add them up.. code for this is too long so just figure it out yourself..

(or use one of those cheesy plasma cloud algorithms where you subdivide layers of random interplated values to get the desired result)
EDIT: And as requested, here is the subplasma routine..function subplasma(seed,size,amplitude);
{
rndseed=seed;
x1=0;
while x1<256
{
y1=0;
while y1<256
{
offset=x1+y1 shl 8;
texture[offset]=rnd(amplitude);
y1=y1+size;
}
x1=x1+size;
}
if size<=1 then exit;
x1=0;
while x1<256
{
y1=0;
while y1<256
{
corner[0] = texture[(x1) + (y1) << 8];
corner[1] = texture[(x1+size) + (y1) << 8];
corner[2] = texture[(x1) + (y1+size) << 8];
corner[3] = texture[(x1+size) + (y1+size) << 8];
for x2=1 to size do
for y2=1 to size do texture[(x1+x2)+byte(y1+y2) << 8]=cosineinterpolate(corner,x2/size,y2/size);
y1=y1+size;
}
x1=x1+size;
}
}
and now for the cosine interpolation(personally, i think a catmull rom spline gives alot better results but cosine interpolation is faster)..
function CosineInterpolate(corners:array;x,y);
{
val= 0.0;
f1 = (1.0 - cos(x * pi))*0.5;
f2 = (1.0 - cos(y * pi))*0.5;
f1 = 1.0-f1;
f2 = 1.0-f2;
fraction[0] = f1*f2;
fraction[1] = (1.0-f1)*f2;
fraction[2] = f1*(1.0-f2);
fraction[3] = (1.0-f1)*(1.0-f2);
for i=0 to 3 do val = val+corners*fraction;
result = val;
}
now, lets move on to distortions..
basically, to distort something, you look at your source pixel, then you use a map or algorithm to distort your source pixel, and then you plot your pixel.. there are a few useful ones, like offset, map distort, sine distort, directional blur distort, twirl etc.. and i'll try to cover the most basic ones here..
Distorting Part [1]
normal map distort... basically, you run through each and every pixel in your source map, and assign it a new X/Y value based on a value in your MAP texture, for example:
for each X
for each Y
{
x2 = distmapX[x+y << 8];
y2 = distmapY[x+y << 8];
color=texture[(x+x2)+(y+y2) << 8];
new_texture[x+y]=color;
}
now, you can use it out of the box, or you can scale it by dividing the X2 and Y2 values to get better control of the strenght.. the above way is not how i do it, but hey, you need to figure some stuff out on your own

Distorting Part [2]
ok next we have the sin/cos distort. no pseudo for this one since it's so easy to do.. if you managed to get the sin/cos plasma working you shouldnt have any troubles implementing this

Basicaly, think of it as a sin/cos plasma, only you use the sin/cos values to distort your original texture instead of plotting the color values..
Distorting Part [3]
offset.. as easy as it gets.. just add to X and/or Y.. remember, make it tile

Distorting Part [4]
Directional Blur..
Basically, what this is is, a Photoshop style motion blur.. only difference is, instead of using a static value for direction you pick a value from a texture map.. kinda anoying to explain, so here's some pseudo:
dd = (pi*2)/256
d = 0 to 255 do
{
vectors[(count*2)+0] = sin(pi-d);
vectors[(count*2)+1] = cos(pi-d);
d = d+dd;
}
for each x do
for each y do
{
degree = texturemap[x+y << 8]; // (this is where you set your own value in the range from 0..255 to make a normal motion blur)
fx=x;
fy=y;
count = 0 to distance do
{
fx = fx + vectors[(degree*2)+0];
fy = fy + vectors[(degree*2)+1];
v2 = v2 + bilinear(fx,fy);
}
color = round(v2/distance);
texture[x+y << 8]=color;
v2 = 0;
}
ok, thats it for distorting.. now for some simple Layer operations..
I wont be doing "Chapters" for this, since it's all so easy to understand and basically no pseudo is required..
ok, im just gonna make a quick list of operations..
for each X/Y, go thru it and apply the operators to the textures like:
texture1[x,y << 8] "operator" texture2[x,y << 8]
...
add : color=texture1[x+y << 8]+texture2[x+y << 8]
sub : color=texture1[x+y << 8]-texture2[x+y << 8]
mul : color=texture1[x+y << 8]*texture2[x+y << 8]
div : color=texture1[x+y << 8]/(texture2[x+y << 8]+1) (to avoid division by zero)
neg : finaltexture=255-texture1[x+y << 8]
gry : finaltexture=(texture1.R[x+y << 8]+texture1.G[x+y << 8]+texture1.B[x+y << 8])/3
sin : color = sin(texture[x+y >> 8]*255);
the list goes on and on.. you can use this as either a base for your own texture generator, or as a reference to improve an existing one.. or as a way of finding new ways of doing stuff..
keep in mind that my shifts (<< 8 ) are for 256x256 textures.. since that is what im working with in my own generator.. (a left shift by 8 is the same as multiplication by 256, only ALOT faster)
I hope you guys find this useful.. keep in mind i wrote it while i was drunk as ***** so some of it might be slightly inaccurate and you will have to fiddle around with it for a while to make it work, but hey, thats 90% of the fun

ADDED: When i use "Bilinear(X,Y) you can just go "color = int texture[X,Y << 8 ]" instead.. i use bilinear filtering for all my distortions to get rid of ugly artifacts (since all my distortions use floats and not ints, for precision).
Thats it for this tutorial.. hope you enjoy it..
-----------------------------------------------------------------------------------------------------------------------------------------------
There ya go guys.. hope you can make some use of it.. it's old stuff but i figured not everybody knows how to generate textures, and this might get some others started
