Waveforms are really useful shaping functions. They can be used for ripples, oscillations, and of course Vexember challenges.
Here's some basic information on waveforms, plus some common waveforms you might find useful.
Waveforms have 3 main parameters we can mess with: the amplitude, frequency and phase.
Amplitude is the height of the wave. It controls the volume if the sound is heard.
An amplitude of 0 means you can't hear the sound at all, while 1 means it plays at full blast.
When the amplitude is 1, the range of the wave is usually -1 to 1, centered at 0. The center is called the DC offset.
You control the amplitude by multiplying the wave:
float wave = sin(time) * amplitude;
Frequency is how often the wave repeats. It controls the pitch if the sound is heard.
Each repeat is called a cycle. Sine and cosine waves complete a full cycle every 2*PI
radians (360 degrees).
You control the frequency by multiplying the time:
float wave = sin(time * frequency);
Since it's easier to use normalized units (0 to 1) than radians (0 to 2*PI
), multiply by 2*PI
to repeat every unit instead:
float wave = sin(time * frequency * 2 * PI);
Phase is how far along we are in the wave's cycle. Visually it looks like a time offset.
Conceptually it's more like a rotation.
You control the phase by adding to the time:
float wave = sin(time + phase);
Putting it all together, you can use the following formula to control amplitude, frequency and phase:
float wave = amplitude * sin(time * frequency + phase);
Sine and cosine are identical, except for a phase difference of PI/2
.
You can swap sine and cos by changing the phase:
float cos = sin(time + PI / 2);
float sin = cos(time - PI / 2);
Here's a bunch of common waveforms. I originally made them for music on ShaderToy, but they're great for Houdini too.
- Each waveform is phase aligned, meaning the positive and negative cycles match. This prevents interference when they're added together.
- Each waveform is centered with a DC offset of 0.
- Each waveform has an amplitude of 1, with each sample ranging from -1 to 1. Multiply to control the volume.
- Each waveform's phase is controlled by adding to the time.
To use one, copy paste it into a wrangle and call it like so:
float waveSine(float freq; float time) {
return sin(2 * PI * freq * time);
}
// Sine wave which repeats every second
v@P.x += waveSine(1, f@Time);
float waveSine(float freq; float time) {
return sin(2 * PI * freq * time);
}
Add 0.25 to time
to get a cosine wave.
float waveCosine(float freq; float time) {
return cos(2 * PI * freq * time);
}
Subtract 0.25 from time
to get a sine wave.
float waveSquare(float freq; float time) {
return ceil(0.5 - frac(freq * time)) * 2 - 1;
}
I used ceil()
since it seems error prone than sign()
.
float waveTriangle(float freq; float time) {
return abs(frac(freq * time - 0.25) - 0.5) * 4 - 1;
}
I used frac()
since it's faster than modulo.
float waveSaw(float freq; float time) {
return frac(freq * time + 0.5) * 2 - 1;
}
float wavePulse(float freq; float time; float duty) {
return (frac(freq * time) < duty) * 2 - 1;
}
duty
controls how much of the wave is positive. It should be between 0 and 1.
Set duty
to 0.5 to get a square wave.
float waveCustom(float freq; float time) {
return chramp("custom_waveform", frac(freq * time + 0.5)) * 2 - 1;
}
Draw a waveform with a ramp. Samples are between 0 and 1.