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, 2013 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, 2013 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, 2013 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\$ Jul 5, 2013 at 12:46

1 Answer 1


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, 2018 at 3:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .