[Android] Having More Fun: Explore Instagram-like Filters with Photoshop and OpenGL ES Shaders

The original post is moved to my new personal blog



  1. […] With a Glass of Wine has been having a lot of fun with OpenGL ES fragment shaders, coming up with some really neat effects. […]

  2. Yingyun · · Reply

    Nice work, it give me some hint .
    Do you have any idea about the implementation of android animation by OpenGL ?

    1. I am working more on image processing and vision, currently not so good at animation, but try to learn as well. 🙂

  3. […] project – image processing using OpenGL ES shaders. (Previous post: basic image processing, instagram filters, live streaming), I studied and implemented another two interesting filters: artistic painting […]

  4. Mark Ortiz · · Reply

    Hi Lu, I hope you are willing to help me with a texture problem?

    I’d like to be able to colour a texture (I know how to do this), but preserve the luminance – that is keep the whites white, and the blacks black.

    My code is currently:

    gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); //RED

    This *almost* works, as I can set a texture to any colour that I want. However the bright parts still appear the colour, not white.



    …for pictures of what I mean (that code doesn’t work for me – I think because it’s iPhone)

    If you were able to add another filter to your github: GSTest that does this, it’ll be very much appreciated!!

    Thanks for any advice!

    1. Hello Mark, I hope I get this right: what you want to do is to BLEND the color with the texture (http://en.wikipedia.org/wiki/Blend_modes). There are different blend modes, and I suppose overlay blending should be used.

      Here is the function I wrote for overlay blend in fragment shader:

      vec3 ovelayBlender(vec3 Color, vec3 filter){
      vec3 filter_result;
      float luminance = dot(filter, W);

      if(luminance < 0.5)
      filter_result = 2. * filter * Color;
      filter_result = 1. – (1. – (2. *(filter – 0.5)))*(1. – Color);

      return filter_result;

      And you know you should work with OpenGL 2.0 to enable shaders.

  5. Mark Ortiz · · Reply

    Hi – thank you so much for replying! I’ve done what I want to do in Photoshop, so as to show what I mean.

    Let us suppose that I start with: http://i40.tinypic.com/2znua1d.jpg

    I can use this as a texture with GL_MODULATE and glColor4f(1.0f, 0.0f, 0.0f, 1.0f) to get:

    However, this is red and black – there is no white.

    I’ve simulated what I want with Photoshop (, Colorize, Saturation = 100)

    Please do not worry if you do not understand what I mean. I’m already very grateful for your previous reply. Thank you, once again.

  6. Mark Ortiz · · Reply

    I’ve just realised that OpenGL 1 and 2.0 are completely different! 😦

  7. Mark Ortiz · · Reply

    For this article, would you mind showing your ‘main()’ [or even the whole fragment shader?] and how it calls overlayBlender / Blue / BrightnessContrastSaturation? – I think then I may be able to work out what is happening. Thank you!!

    1. precision mediump float;
      uniform sampler2D u_Texture0;
      uniform sampler2D u_Texture2;
      varying vec2 v_TexCoordinate;
      const vec3 W = vec3(0.2125, 0.7154, 0.0721);

      vec3 BrightnessContrastSaturation(vec3 color, float brt, float con, float sat)
      vec3 black = vec3(0., 0., 0.);
      vec3 middle = vec3(0.5, 0.5, 0.5);
      float luminance = dot(color, W);
      vec3 gray = vec3(luminance, luminance, luminance);
      vec3 brtColor = mix(black, color, brt);
      vec3 conColor = mix(middle, brtColor, con);
      vec3 satColor = mix(gray, conColor, sat);
      return satColor;

      vec3 ovelayBlender(vec3 Color, vec3 filter){
      vec3 filter_result;
      float luminance = dot(filter, W);
      if(luminance < 0.5)
      filter_result = 2. * filter * Color;
      filter_result = 1. - (1. - (2. *(filter - 0.5)))*(1. - Color);
      return filter_result;

      void main()
      //get the pixel
      vec2 st = v_TexCoordinate.st;
      vec3 irgb = texture2D(u_Texture0, st).rgb;
      vec3 filter = texture2D(u_Texture2, st).rgb;
      //adjust the brightness/contrast/saturation
      float T_bright = 1.3;
      float T_contrast = 1.0;
      float T_saturation = 1.3;
      vec3 bcs_result = BrightnessContrastSaturation(irgb, T_bright, T_contrast, T_saturation);
      //more red, less blue
      vec3 rb_result = vec3(bcs_result.r*1.15, bcs_result.g, bcs_result.b*0.8);
      //add filter (overlay blending)
      vec3 after_filter = mix(rb_result, ovelayBlender(rb_result, filter), 0.8);
      gl_FragColor = vec4(after_filter, 1.);

      This is the fragment shader for “Hefe” effect. It basically adjust the brightness/contrast/saturation first, then it overlay blend a layer of gradient color texture on top of the photo texture.

  8. Mark Ortiz · · Reply

    Thanks! bright =1, saturation = 5 / bc_result.r*5 does pretty much what I needed! Thank you so much.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: