Sine Wave Zion - Additive Colour Demo in Shadertoy

Chilin - my African friend asked me if I could make a shader to visualise a sound or sine wave with harmonics included, so I wrote this WebGL shadertoy:

In the end, I didn't quite get there but this shader makes a nice illustration of the theory of additive colours, as various offsets of the sine waves are given their own channel: Red for the main frequency, Green for the second harmonic or octave, and Blue for the third harmonic.

Features: sine waves, pi, mouse input to alter parameters, interference patterns

Highlights of the code

This is just a normalised sine function of time multiplied by the pixel co-ordinates being rendered.

vec2 S; // speed of fundamental
S.x = (xy.x + xy.y)*(xy.x - xy.y)*0.5; // "velocity potential" a square of the pixel distance so it gets big quick
S.x -= iGlobalTime *timespeed; // animate stream
vec2 sxy = sin(3.14159 * S * 1.0 ); // its a sine wave of time and pi

The Full Code

// Concept: an animation of wave harmonics
// show 1st, 2nd, 3rd order harmonics somehow

float divs = 2.2;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec4 m = iMouse; 
if ( m.x == 0. && m.y == 0.) {
m.x = iResolution.x / 2.;
m.y = iResolution.y / 2.;
}
float dist = m.x - ( iResolution.x / 2.0 ); // centererd
float speed = 0.0009; // 0.9 camera movement to plane 
float timespeed = 0.1 ; // 0.1 wave movement on the ribbons
  
// MOUSE: multiple by my or mx to get value between 1 and 2
float my = float( (m.y + iResolution.y ) / iResolution.y );
float mx = float( (m.x + iResolution.y ) / iResolution.x );

float t = 8.0 - speed ;

// divs create staggered / staged division, old setting was divs += t / 0.20;
// let the mouse distance control the zoom
divs += t / ( ( m.x + 2. ) / iResolution.x );
 
vec2 div = vec2( divs, divs*iResolution.y/iResolution.x );
vec2 uv = fragCoord.xy / iResolution.xy;
 
// center on screen then pan off:
uv -= 0.1 + (iGlobalTime*0.0001); 
 
float b = 1.0*divs/iResolution.x; // blur over 2.4 pixels

vec2 xy = div*uv;
 
vec2 S; // speed of fundamental
vec2 S2; // speed of 1st octave
vec2 S3; // speed of 2nd harmonic (perfect fifth?)


S.x = (xy.x + xy.y)*(xy.x - xy.y)*0.5; // "velocity potential"
S2.x = (xy.x + xy.y)*(xy.x - xy.y)*0.5; // "velocity potential"
S3.x = (xy.x + xy.y)*(xy.x - xy.y)*0.5; // "velocity potential"

S.y = xy.x*xy.y; // stream function
S2.y = xy.x*xy.y; // stream function 
S3.y = xy.x*xy.y; // stream function


// speed of the dots
S.x -= iGlobalTime *timespeed; // animate stream
S2.x -= iGlobalTime *timespeed + (my * 20. ); // animate stream
S3.x -= iGlobalTime *timespeed + (my * 20.); // animate stream

 
 
// HERE IS THE WAVE HARMONICS 
// SXY IS FUNDAMENTAL WAVE2 IS 2ND HARMONIC
// sxy is *probably* the main wave
// wave2 is the 2nd harmonic
 
vec2 sxy = sin(3.14159 * S * 1.0 );
vec2 wave2 = sin(3.14159 * S2 * 2.0 ); // 2.00 is double
vec2 wave3 = sin(3.14159 * S3 * 3.0 ); // 3.00 is double
 
 
// w2 is the 2nd harmonic
float a = sxy.x * sxy.y; // combine sine waves using product
float w2 = wave2.x * wave2.y; // combine sine waves using product
float w3 = wave3.x * wave3.y; // combine sine waves using product
 
// not sure what this does but we will do it to w2, w3 as well
a = 0.5*a + 0.5; // remap to [0..1]
a = smoothstep( 0.85-b, 0.85+b, a ); // threshold
 
w2 = 0.5*a + 0.5; // remap to [0..1]
w2 = smoothstep( 0.85-b, 0.85+b, a ); // threshold
 
w3 = 0.5*a + 0.5; // remap to [0..1]
w3 = smoothstep( 0.85-b, 0.85+b, a ); // threshold
 
float c = sqrt( a ); // correct for gamma
float w2gamma = sqrt( w2 ); // correct for gamma
float w3gamma = sqrt( w3 ); // correct for gamma
 
float red, green, blue;
 
red = sxy.x - sxy.y ;
green = wave2.x - wave2.y;
blue = wave3.x - wave3.y;
 
float crossover = -1.5;
 
if (red < crossover) {
red = red * -1.;
}
if (green < crossover) {
green = green * -1.;
} 
if (blue < crossover) {
blue = blue * -1.;
} 
 
fragColor = vec4( red, green, blue, 1.0);

}
Posted by tomachi on March 17th, 2017 filed in Visuals