after that I'd say playing with trig functions (sin, cos, tan) and looking up noise layers would be a good next step, all you realy need to know is sin and cos go up and down in a smooth fashion (and the replete based on multiples of PI so multiplying something in the 0-1 range by PI would probably be a good idea) tan tends to go crazy at regular intervals, so it can with some trickery be made to make something that looks sort of like lightning when combined with a noise layer. which I'll explain how to do later in this post.
this isn't so much a tutorial as an explanation of a few techniques I developed, I give example code and you can just copy it. I guess I should start with some reference material.
modifying this old post for some changes in syntax
most variables go in the range of 0-1,
x, y, r, and t go from 0 to 1 (r actualy goes to sqrt(2) in the corners)
X and Y (note capitalisation) go from -1 to 1
ang goes from 0 to 2*PI
note if you find this isn't how things are working it's a bug. I'm looking into something right now that makes me think ang might be between -PI and PI, I might keep it this way though, what do you think?
I figured these would be the most maliable ranges for these, cause if you wanted a function that changed, say, from -2 to 11, along lets say x, you'd just have to set a variable like this
@NEWX=x*13-2
and instead of x you'd use NEWX
all output chanels are in the range of 0 to 1, were 0 is totaly off and 1 is totaly on (exept hue wich wraps around, 0 and 1 are red, 0.5 is green 0.75 is blue/purple, 0.25 is yellow/green, hsl is sort of like polar coordanants with H being equivelent to theta).
so you need to normalise your output to this, on drawing anything greater than 1 is clamped to 1, and less than 0 to 0, now this is after evaluation, so you can have negitive values, it's just that when it's drawn on screen it shows up as black. you can invoke this function in your code if you think it will be usefull (anything with a divide over (aproching) zero in it for instance, like tangent functions) with clamp().
I was also considering adding iterative abilities (wich could allow you to fake diferentiation/integration and do series convergence) but that's going to have to wait untill after I figure out how to get user defined functions to work (and I don't mean those psudo functional variables
[edit]this is in now[/edit]
also a few hidden functions that use non-standard notation (because its a requierment of the odd syntax I started with)
b#x == the b'th root of x
b~x == the log base b of x
comparison opperators (>,<,=) return 1.0 for true and 0.0 for false
and modulus is %
also there are built in constants PI, PHI, and E (case sensitive, I know e is suposed to be lower case but it's my screwy syntax) if there are any others you'd like I'd be happy to include them. there is also a built in variable called srand wich is a static random, it gets changed everytime you evaluate to something between 0 and 1 (it would be diferent in each frame of an animation)
while I'm at it here is a complete list of all unary functions currently supported:
hypsin
hypcos
hyptan
arcsin
arccos
arctan
sin
cos
tan
log
ln
sqrt
deg (degree to radian)
rad (radian to degree)
! (factorial, it works a bit oddly !(var) rather than var! as you would expect)
abs
ipart (returns only the whole number)
fpart (strips off whole numbers, leaveing only the less than 1 part)
sign (returns -1 or 1)
not (returns 0 if you pass it 1, zero under all other cases)
clamp (returns 1 if passed over 1, 0 if under 0)
if this starts getting used I'm likely to rework the syntax
oh and if you plan on doing animations here is a tip, evaluate at t=0 and t=1 it the two images look exactly the same it will loop seamlessly.
all functions take one parameter within parentheses, of which the opening one touches the function name (though white noise is ignored, so if there is a space or tab it would probly still work).
that's a fairly extensive list of the built in functions.
layers work pretty much like variables, once you make a layer it can be referenced any ware, when you reference a layer you do it by looking up color components at a relative point (0-1 for x and y) so if you want to find the luminance of a layer named 'example' it would look like example.l(0.5,0.5), you can look up just about anything about any color component, r is red, g is green, b is blue, l is luminence, h is hue, s is saturation (these last two don't seem to work properly), there's a few others I've never tested for the other color spaces I've made. there is currently no support for reevaluateing at a given point, but I'll probly include that at some point in the near future. in addition to looking up other layers you can define a local layer that gets referenced exactly the same way as normal layers but is only available in the layer it is defined for (I think I described the syntax for this in this thread already). if you want to look up the value of another layer at the exact same point (relitively) in that layer it would look like example.l(x,y), layer lookup works a like like UV map lookup, so if you understand UV mapping, then this should be a peice of cake. you can do distorted lookups for example repeteing another image twice horizontaly (along the x axis) would look like this example.l(2*x,y). distorted lookups are sort of an advanced effect, and you can easily get some stunning results with it, but it usualy needs tiled noise to work properly.
now useing layer lookups you can make all sorts of noise, a layer wich is made just so it can be looked up for noise is what I refer to as a noise layer, Water posted a pic from a turbulence noise layer, so far I have figured out two very nice noise layers the first is simple perlin noise it's omnidirectional cloudy fogy stuff it's good for all sorts of stuf were you just want some variation in color that is random but does not look like static. I posted all this code in the weapon effect thread, but I'll try to consolidate here.
good perlin noise
$N[512,512]<RGB>{
@A=rand(1)
@B=rand(1)
@C=rand(1)
}<A|B|C>
@R=sum(test(i<513),var(l,0,l+N.r(x/i+x/(2*i),y/i+y/(2*i))/(512/i)),var(i,1,i*2))
@G=sum(test(i<513),var(l,0,l+N.g(x/i+x/(2*i),y/i+y/(2*i))/(512/i)),var(i,1,i*2))
@B=sum(test(i<513),var(l,0,l+N.b(x/i+x/(2*i),y/i+y/(2*i))/(512/i)),var(i,1,i*2))
this will give you perlin noise that can then be looked up in other layers, you can multiply by it or add it to some other variable and it usually has a pretty significant effect on things, using noise and other variables inside trig functions can have nice effects too. unfortunately this is not in it's current form tillable, and the method I use for making it tillable tends to cause it to lose resolution so you should start off with noise twice as big as you think you are going to need. but before I go into tileing methods and other stuff I should mention turbulence noise.
good turbulent noise
$N[512,512]<RGB>{
@A=rand(1)
@B=rand(1)
@C=rand(1)
}<A|B|C>
@R=sum(test(i<513),var(l,0,l+((N.r(x/i+x/(2*i),y/i+y/(2*i))-0.5)*2)^(1/4)/(1024/i)),var(i,1,i*2))
@G=sum(test(i<513),var(l,0,l+((N.g(x/i+x/(2*i),y/i+y/(2*i))-0.5)*2)^(1/4)/(1024/i)),var(i,1,i*2))
@B=sum(test(i<513),var(l,0,l+((N.b(x/i+x/(2*i),y/i+y/(2*i))-0.5)*2)^(1/4)/(1024/i)),var(i,1,i*2))
this will produce that wispy vainy effect and is the heart of the detail in the terran beams I made (the vasudan beams use perlin noise with a distorted lookup) like the perlin noise it too is non tileable in it's natural state, which leads us to the next topic, tileing layers.
good thing to make stuff seamless along one axis
$N[512,512]<RGB>{
@RX=WN.r(x,y)*(1-abs(X))+WN.r(x+0.5,y)*abs(X)
@RY=WN.r(x,y)*(1-abs(Y))+WN.r(x,y+0.5)*abs(Y)
@GX=WN.g(x,y)*(1-abs(X))+WN.g(x+0.5,y)*abs(X)
@GY=WN.g(x,y)*(1-abs(Y))+WN.g(x,y+0.5)*abs(Y)
@BX=WN.b(x,y)*(1-abs(X))+WN.b(x+0.5,y)*abs(X)
@BY=WN.b(x,y)*(1-abs(Y))+WN.b(x,y+0.5)*abs(Y)
}<RX|GX|BX>
@R=N.r(x/2,y)
@G=N.g(x/2,y)
@B=N.b(x/2,y)
this code is a general form for making a non-tiling image tile along one axis, specifically it's set up to tile along the x axis, but it has the variables setup to go along the y axis also (so you can change it easily), it expects another layer called WN that has the source noise, what it does is it looks up the source noise and fades it along the edge, then it adds to that the source noise moved over by half also faded at the edges, but sence it's moved over it cancels out the fading of the other lookup, the big downside is only the detail in the very middle of the image realy survives this process. of course you don't realy need to know how it works to use it, just paste one of the noise layer codes into a layer named WN and then paste this into a separate layer and you'll have noise that tiles along the x axis asymmetrically.
there are some other tileing techniques you can use that work via distorted lookups, my favorite is sin distorted lookup, it works like thus.
rather than looking up your noise directily like this example.l(x,y), you look it up like this example.l(sin(x*PI)*0.5+0.5,sin(y*PI)*0.5+0.5), now you might look at that and go "OH MY GOD HES GOING INTO QUANTUM CALCULUS!!!" no just settle down and look at it for a second, for the two lookup parameters you have basicly the same function of x or y, that function is this "sin(x*PI)*0.5+0.5", now as I mentioned earlier all you need to know about sin and cos is they move smoothly between -1 and 1 and they repete based on multables of PI, so we have x*PI, so it will repete with x now, we have *0.5, now it's in the range of -0.5 to 0.5, then finaly we have +0.5 wich shifts the range to 0 to 1, wich is the whole domane of an image, so this will basicly move back and forth over an image.
but of course just like everything else you don't need to understand how it works to use it, just see how it looks and from now on you can just copy and pase when you want that effect now sinlookup has a problem in that there are periodic places were the sin function barely changes, and so you will get basickly lines in your output, your can fenegle this out if you play with it enough, but a lot of times you can just hide it.
a similar tequnique for tiling lookup involves inverse sin as well, example.l((arcsin(sin(x*PI))/PI)*0.5+0.5,(arcsin(sin(y*PI))/PI)*0.5+0.5)
as before this looks a lot more complicated than it is, all we do here is multiply the input variable by PI so the function repetes along it, then we take the sin of that, then the inverse of sin, but it's going to be multables of PI so we divide that out, then like before with have something in the -1 to 1 range, so we make the same adjustment. the sesult is it moves back and forth over the image at a constant rate, this get's rid of the line problem in the standard sin lookup, as well as the result is not distorted (wich is unfotunate if you _wanted_ distortion), but mirors every other tile.
and like before all you need to do is copy and paste, not understand the inner workings. when stuf starts to get realy long it might be a good time to start useing other variables to simplify things.
so rather than
@L=(1-r)^4*example.l((arcsin(sin(x*PI))/PI)*0.5+0.5,(arcsin(sin(y*PI))/PI)*0.5+0.5) + example.l((arcsin(sin(x*PI))/PI)*0.5+0.5,(arcsin(sin(y*PI))/PI)*0.5+0.5)
you could have
@N=example.l((arcsin(sin(x*PI))/PI)*0.5+0.5,(arcsin(sin(y*PI))/PI)*0.5+0.5)
@L=(1-r)^4*N+N
//I have no idea what this code would acualy do I just made it up. but it's easier to read, yes?
once you have a tileing noise solution, you can also make use of more interestin lookup distortions, for example
exampl.l(ang/PI,r)
what this does is it wraps the example layer in a circle around the center of the screen, now for a static image this would only need to tile along x (wich is getting looked up via the angle), but if you were going to animate it and have it pull inward then you'd want it tiled along y as well a combination of this and the arcsin lookup was the basis of
this effect, the noise used was simple perlin, I made sevral radial lookups with arcsin tilling (of r*PI and ang rather than x*PI and y*PI)
I should probly work out a simple step by step tutorial for makeing something specific...