How would you implement the effect of chromatic aberration with shaders?

Would rendering of the world with different focus distances for each color solve the problem (maybe with the usage of only one depth rendering pass)?

  • \$\begingroup\$ Why would you want to implement chromatic abberation? \$\endgroup\$ – ashes999 Jul 1 '13 at 14:14
  • 3
    \$\begingroup\$ You could proceed the same way as bloom is achieved. You should combine your normal scene with 3 blurred versions of it; each of these three textures should be tinted predominantly in the 3 basic colors.. Of course, variations are possible if you use different blur strategies for the 3 textures (e.g. directional blur with different angular offsets might do the trick). \$\endgroup\$ – teodron Jul 1 '13 at 14:26
  • 3
    \$\begingroup\$ @ashes999 - Perhaps a sniper scope or other long lens, to add a touch of realism? Chromatic aberration is an artifact of many compound lens designs. \$\endgroup\$ – KeithS Jul 1 '13 at 18:27
  • \$\begingroup\$ You may also want to implement chromatic aberration to counter any aberration caused by a lens through which the game is being viewed. For example, it is a good idea to have chromatic aberration at the edges of the view in a game played with the Oculus Rift. \$\endgroup\$ – Joseph Mansfield Jul 5 '13 at 12:46

Chromatic aberration is caused when a lens can't focus every color to the same focal point. A simple way to fake this effect, and render it as a quick full-screen post-process, is to apply an offset to each color channel in a fragment shader.

By using a different offset for each channel, you can achieve a reasonable facsimile of the desired effect. An example of this technique can be found here; the fragment shader would look something like this:

void main () {
    // Previously, you'd have rendered your complete scene into a texture
    // bound to "fullScreenTexture."
    vec4 rValue = texture2D(fullscreenTexture, gl_TexCoords[0] - rOffset);  
    vec4 gValue = texture2D(fullscreenTexture, gl_TexCoords[0] - gOffset);
    vec4 bValue = texture2D(fullscreenTexture, gl_TexCoords[0] - bOffset);  

    // Combine the offset colors.
    gl_FragColor = vec4(rValue.r, gValue.g, bValue.b, 1.0);

This simple hack doesn't really take into account the fact that chromatic aberration is a lens effect, though: to get a better simulation, you'd actually want to render something to act as the lens. This is similar to how you render objects that are reflective or refractive. Consequently, a typical reflection/refraction shader can be the basis for implementing chromatic aberration.

Normally, you'd compute a single refraction vector based on a view vector and some defined index of refraction, using GLSL's refract function in a vertex shader:

void main () {
    // ...

    // RefractionVector is a varying vec3.
    // 'ratio' is the ratio of the two indices of refraction.
    RefractionVector = refract(incidentVector, normalVector, ratio);

    // ...

Then you'd use that vector in a fragment shader to perform an cube texture lookup (into an environment map). Typically this is done alongside a reflection effect as well, and combined used a computed Fresnel term.

To simulate chromatic aberration, then, you can perform three different refraction vector computations, each one slightly offset via differing indices of refraction, in the vertex shader:

void main () {
    // ...

    // RefractionVector is a varying vec3, as above.
    // 'ratioR,' et cetera, is the ratio of indices of refraction for
    // the red, green and blue components respectively.
    RedRefractionVector = refract(incidentVector, normalVector, ratioR);
    GreenRefractionVector = refract(incidentVector, normalVector, ratioG);
    BlueRefractionVector = refract(incidentVector, normalVector, ratioB);

    // ...

Those three different vectors can be used to perform three different cube map lookups, which can be mixed together similar to how the colors were mixed in the simple example:

void main () {
    vec3 color;
    color.r = vec3(textureCube(EnvironmentMap, RedRefractionVector)).r;
    color.g = vec3(textureCube(EnvironmentMap, GreenRefractionVector)).g;
    color.b = vec3(textureCube(EnvironmentMap, BlueRefractionVector)).b;

    gl_FragColor = vec4(color, 1.0);

For more details, the OpenGL Orange Book is available and contains an example of the basic reflection and refraction effects, as well as an example of the chromatic aberration effect.

  • \$\begingroup\$ Is there an approach that uses only one texture sample and just tints it? It would be very beneficial for applications like ray tracing, where tracing three vs one ray is very expensive. \$\endgroup\$ – Tara Mar 27 '18 at 3:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.