JoyOfVex2

From cgwiki

Day 2

Vex has lots of functions. LOTS. Here's an alarming list of them all:

http://www.sidefx.com/docs/houdini16.0/vex/functions/_index

Good to start with the simple, work up. A handy starting point is to measure the length of a vector, which you do with length. (Keep with the grid geometry for now)

 float d = length(@P);

Applied to @P, it tells you the distance a point is from the origin. Put that into colour:

 float d = length(@P);
 @Cd = d;

Boring. But we can now put this into sin, get something interesting:

 float d = length(@P);
 @Cd = sin(d);

will probably need to set the range to see the sine waves better:

 float d = length(@P);
 d *= ch('scale'); 
 @Cd = sin(d);

3 lines of vex! woo! Set the scale slider above 1, somewhere between 5 and 10 on the grid to see the waves doing their thing.

Those colour values are still dipping into negative though, don't like that. Can just add an offset to get them out of negative.

 float d = length(@P);
 d *= ch('scale'); 
 @Cd = sin(d)+1;

But our colours still aren't right, as now we're getting values going between 0 and 2. Now we can divide by 2 to get it back to a 0 to 1 range (and use brackets to make sure the operations are done in the right order):

 float d = length(@P);
 d *= ch('scale'); 
 @Cd = (sin(d)+1)/2;

Some coders frown upon using divide, and prefer to multiply by a fraction. You look cooler if you do, but it doesn't really matter at this stage:

 float d = length(@P);
 d *= ch('scale'); 
 @Cd = (sin(d)+1)*0.5;

Breaking this across 3 lines is a matter of style, its perfectly valid to compact lines together, in whatever way you want. I like to break them out, others write super compressed one liners, all good. This is the same:

 float d = length(@P) * ch('scale');
 @Cd = (sin(d)+1)*0.5;

and this (but see how it gets harder to read at a glance):

 @Cd = (sin(length(@P) * ch('scale'))+1)*0.5;

Related to length is distance. While length(@P), measures the distance from the point to {0,0,0}, distance lets you define the start point, so we could have these rings start at {1,0,3} for example. That's how you type simple vectors, with curly brackets btw. One of the most common errors you make in vex when starting out, and still when you get experienced, is accidentally swapping curly brackets for normal ones!

 float d = distance(@P, {1,0,3} );
 d *= ch('scale'); 
 @Cd = (sin(d)+1)*0.5;

There's another channel call, chv, that gives you a vector slider. Lets define a vector variable, 'center', use that:

 vector center = chv('center');
 float d = distance(@P, center );
 d *= ch('scale'); 
 @Cd = (sin(d)+1)*0.5;

An interesting trick is to multiply vectors. For example, multiply @P by {0.5,1,1}, you're making x advance half as fast as it normally does. Feed that in rather than vanilla @P, you get squished rings:

 vector center = chv('center');
 float d = distance(@P* {0.5,1,1}, center );
 d *= ch('scale'); 
 @Cd = (sin(d)+1)*0.5;

Again my tidy gene wants to kick in and make this easier to read at a glance, as well as easy to edit interactively with another slider:

 vector pos = @P * chv('fancyscale');
 vector center = chv('center');
 float d = distance(pos, center );
 d *= ch('scale'); 
 @Cd = sin(d)*0.5+1;

Note that I originally had both chv('scale') on the first line, and ch('scale') on the 2nd last line, which is wrong. You can't use identical names for channels, one will take priority. Moreso, once you've defined a channel by name (say from the previous example), and you change its type in vex (from ch to chv say), the 'make channels' button won't update it.

The laziest way to fix it is to delete all the channel sliders/parameters, and click the 'make channel' button again to recreate it. You can delete all the channels in one hit from the parameter menu, and choose 'delete all spare parameters'. You lose all the values you might have typed in, which is a shame, but its a lesson to not clash your names in future. :)

That add one and divide by 2 step can be replaced by another function, fit. You tell it the incoming min and max values ( -1 and 1 in this case), and the new min and max values you want (0 and 1).

 vector pos = @P * chv('fancyscale');
 vector center = chv('center');
 float d = distance(pos, center );
 d *= ch('scale'); 
 @Cd = fit(sin(d),-1,1,0,1);

And finally, introduce a new handy built in attribute, @Time. If we add this to our d variable, it will push the sine wave along, causing the wave to animate.

 vector pos = @P * chv('fancyscale');
 vector center = chv('center');
 float d = distance(pos, center );
 d *= ch('scale'); 
 @Cd = fit(sin(d+@Time),-1,1,0,1);

To be explicit about a process I'm doing here, you can refer to the same variable or attribute over several lines, and the effects will accumulate if you design them as such. For example we could make these rings be red-on-black by first setting @Cd to black, then setting the red component of @Cd with our sin function:

 vector pos = @P * chv('fancyscale');
 vector center = chv('center');
 float d = distance(pos, center );
 d *= ch('scale'); 
 @Cd = {0,0,0};
 @Cd.r = fit(sin(d+@Time),-1,1,0,1);


Exercises

  1. Change the direction the waves move from towards the center to away from the center
  2. Change the speed of the waves
  3. Make the waves be blue on black, or yellow on green
  4. Rather than affect colour, make them affect the y position of the points.