Workshop GLSL - Gradient - Chapter 6 - Iridescence

Explanations
So let’s change our color model and switch to a color model closest to physical phenomena : Iridescence.
Rainbow is a linear light frequency decomposition. So we need to work with light frequency instead of tint.
#define PROCESSING_COLOR_SHADER
uniform vec2 resolution;
This function convert a wave length to a color.
Wave length is the inverse of frequency so it will be easy to convert frequency to wave length latter.
vec4 wl_to_rgb(float wl)
{
We slice wave length in multiple parts and for each make a gradient. Those values are defined by physical laws
vec3 color;
if(wl < 380.0)
{
color = vec3(1.0, 0.0, 1.0);
}
else if(wl<440.0)
{
color = vec3(-(wl - 440.0) / (440.0 - 380.0), 0.0, 1.0);
}
else if(wl<490.0)
{
color = vec3(0.0, (wl - 440.0) / (490.0 - 440.0), 1.0);
}
else if(wl<510.0)
{
color = vec3(0.0, 1.0, -(wl - 510.0) / (510.0 - 490.0));
}
else if(wl<580.0)
{
color = vec3((wl - 510.0) / (580.0 - 510.0), 1.0, 0.0);
}
else if(wl<645.0)
{
color = vec3(1.0, -(wl - 645.0) / (645.0 - 580.0), 0.0);
}
else
{
color = vec3(1.0, 0.0, 0.0);
}
Then color is scaled to gamma factor (again from physical laws)
float g = 0.80;
vec3 gamma = vec3(g,g,g);
color = pow(color, gamma);
Now since human perseption is limited, we encode visible spectrum in alpha channel : infra red and ultra violet will have no opacity.
float factor;
if((wl >= 380.0) && (wl<420.0)){
factor = (wl - 380.0) / (420.0 - 380.0);
}else if((wl >= 420.0) && (wl<701.0)){
factor = 1.0;
}else if((wl >= 701.0) && (wl<781.0)){
factor = (780.0 - wl) / (780.0 - 700.0);
}else{
factor = 0.0;
}
return vec4(color, factor);
}
Here is our final function scaling from 0-1 to frequencies and converting frequency to wave length
vec4 frequency_to_rgb(float position)
{
We mix from minimum to maximum human visible frequency
float f = mix(385.0, 785.0, position);
Convert it to wave length : 300000 is light speed
float wavelen = 300000.0 / f;
and then return back the final color
return wl_to_rgb(wavelen);
}
void main( void ) {
vec2 position = gl_FragCoord.xy / resolution.xy;
float v = position.x;
So let’s test our rainbow.
vec4 rainbow = frequency_to_rgb(v);
Since we have an alpha channel containing visible spectre, we mix rainbow color with a full white color (representing lightened clouds).
vec3 color = mix(vec3(1.0, 1.0, 1.0), rainbow.rgb, rainbow.a);
gl_FragColor = vec4(color, 1.0);
}
Hopefully, the result looks good.
Full Code Source
#define PROCESSING_COLOR_SHADER
uniform vec2 resolution;
vec4 wl_to_rgb(float wl)
{
vec3 color;
if(wl < 380.0)
{
color = vec3(1.0, 0.0, 1.0);
}
else if(wl<440.0)
{
color = vec3(-(wl - 440.0) / (440.0 - 380.0), 0.0, 1.0);
}
else if(wl<490.0)
{
color = vec3(0.0, (wl - 440.0) / (490.0 - 440.0), 1.0);
}
else if(wl<510.0)
{
color = vec3(0.0, 1.0, -(wl - 510.0) / (510.0 - 490.0));
}
else if(wl<580.0)
{
color = vec3((wl - 510.0) / (580.0 - 510.0), 1.0, 0.0);
}
else if(wl<645.0)
{
color = vec3(1.0, -(wl - 645.0) / (645.0 - 580.0), 0.0);
}
else
{
color = vec3(1.0, 0.0, 0.0);
}
float g = 0.80;
vec3 gamma = vec3(g,g,g);
color = pow(color, gamma);
float factor;
if((wl >= 380.0) && (wl<420.0)){
factor = (wl - 380.0) / (420.0 - 380.0);
}else if((wl >= 420.0) && (wl<701.0)){
factor = 1.0;
}else if((wl >= 701.0) && (wl<781.0)){
factor = (780.0 - wl) / (780.0 - 700.0);
}else{
factor = 0.0;
}
return vec4(color, factor);
}
vec4 frequency_to_rgb(float position)
{
float f = mix(385.0, 785.0, position);
float wavelen = 300000.0 / f;
return wl_to_rgb(wavelen);
}
void main( void ) {
vec2 position = gl_FragCoord.xy / resolution.xy;
float v = position.x;
vec4 rainbow = frequency_to_rgb(v);
vec3 color = mix(vec3(1.0, 1.0, 1.0), rainbow.rgb, rainbow.a);
gl_FragColor = vec4(color, 1.0);
}