[Shader Library] Chromatic Aberration Demo (GLSL)

GeeXLab, Chromatic Aberration (GLSL)

Here is a new GLSL shader for the shader library. This shader implements the lighting dispersion effect also known as chromatic aberration. More details about chromatic aberration can be found here.

The GLSL shader performs the following tasks:

  • light dispersion according to the indices of refraction (IoR).
  • light reflection. The reflection code comes from the following tutorial: Cube Environment Mapping.
  • light refraction. The refraction code is based on the following paper: Fresnel Reflection.

You can download the GeeXLab demo here:

Unzip the archive somewhere and load (or drop) the xml file into GeeXLab (last version can be downloaded HERE). That’s all.

GeeXLab, Chromatic Aberration (GLSL)

GeeXLab, Chromatic Aberration (GLSL)

Here is the source code of the GLSL shader:


uniform vec3 CameraPos;
uniform mat4 ModelWorld4x4;
varying vec3 normal;
varying vec3 E;

mat3 GetLinearPart( mat4 m )
  mat3 result;
  result[0][0] = m[0][0]; 
  result[0][1] = m[0][1]; 
  result[0][2] = m[0][2]; 

  result[1][0] = m[1][0]; 
  result[1][1] = m[1][1]; 
  result[1][2] = m[1][2]; 
  result[2][0] = m[2][0]; 
  result[2][1] = m[2][1]; 
  result[2][2] = m[2][2]; 
  return result;

void main(void)
    // output position
  gl_Position = ftransform();
  // Texture coordinates for glossMap. 
  gl_TexCoord[0] = gl_MultiTexCoord0;
  mat3 ModelWorld3x3 = GetLinearPart( ModelWorld4x4 );
  // find world space position.
  vec4 WorldPos = ModelWorld4x4 *  gl_Vertex;	
  // find world space normal.
  normal = ModelWorld3x3 * gl_Normal; 
  // find world space eye vector.
  E = WorldPos.xyz - CameraPos.xyz;	


vec3 refract(vec3 i, vec3 n, float eta)
    float cosi = dot(-i, n);
    float cost2 = 1.0 - eta * eta * (1.0 - cosi*cosi);
    vec3 t = eta*i + ((eta*cosi - sqrt(abs(cost2))) * n);
    return t * vec3(cost2 > 0.0);

// fresnel approximation
// F(a) = F(0) + (1- cos(a))^5 * (1- F(0))
// Calculate fresnel term. You can approximate it 
// with 1.0-dot(normal, viewpos).	
float fast_fresnel(vec3 I, vec3 N, vec3 fresnelValues)
    float bias = fresnelValues.x;
    float power = fresnelValues.y;
    float scale = 1.0 - bias;
    return bias + pow(1.0 - dot(I, N), power) * scale;

float very_fast_fresnel(vec3 I, vec3 N)
{ return 1.0 - dot(N, I); }

uniform vec3 fresnelValues;
uniform vec3 IoR_Values;
uniform samplerCube envMap;
uniform sampler2D glossMap;
uniform sampler2D baseMap;
varying vec3 normal;
varying vec3 E;

void main(void)
  //------ normalize incoming vectors
  vec3 N = normalize(normal);
  vec3 I = normalize(E);
  //------ Find the reflection
  vec3 reflVec = normalize(reflect(I, N));
  vec3 reflectColor = textureCube(envMap, reflVec).xyz;
  //------ Find the refraction
  vec3 refractColor;
  refractColor.x = textureCube(envMap, refract(I, N, IoR_Values.x)).x;
  refractColor.y = textureCube(envMap, refract(I, N, IoR_Values.y)).y;
  refractColor.z = textureCube(envMap, refract(I, N, IoR_Values.z)).z;
  vec3 base_color = texture2D(baseMap, gl_TexCoord[0].st).rgb;
  //------ Do a gloss map look up and compute the reflectivity.
  vec3 gloss_color = texture2D(glossMap, gl_TexCoord[0].st).rgb;
  float reflectivity = (gloss_color.r + gloss_color.g + gloss_color.b)/3.0;
  //------ Find the Fresnel term
  float fresnelTerm = fast_fresnel(-I, N, fresnelValues);
  //float fresnelTerm = very_fast_fresnel(-I, N);
  //------ Write the final pixel.
  vec3 color = mix(refractColor, reflectColor, fresnelTerm);
  gl_FragColor = vec4(mix(base_color, color, reflectivity), 1.0);

GeeXLab, Chromatic Aberration (GLSL)


  • jammer

    Wouldn’t be faster to use built-in refract() function? It surely computes the same and I think it works on both nVidia and ATI hardware.

  • This shader comes from an old demo that’s why there’s this hand coded refract func. But you’re right, built-in refract() works as well, and it works fine.

  • Very nice demo! As I see now you only split up the color components.
    Of course this is the fastest method but isn’t it possible to have a kind of linear color range that can be split up to make it even look more natural?

  • You could probably compute the wavelength of the input color, and have a different ior for a few ranges of wavelength.

  • Nice shader – thanks!

  • Rich

    Shouldn’t the fresnel be saturate’d before being used in mix ?