1. Hey! Guest! The 35th GMC Jam will take place between November 28th, 12:00 UTC - December 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

Graphics GrayScale Shader

Discussion in 'Tutorials' started by SoulTie, Jul 6, 2016.

  1. SoulTie

    SoulTie Guest

    GM Version: GM Studio
    Target Platform: Windows
    Download: see text / code below


    Summary: Creating a Grayscale effect with Shaders

    Tutorial
    :
    Hi! I wanted to post a short Tutorial on how to write a grayscale shader in GLSL.
    I am not the best teacher, so basic knowledge of shaders is assumed. There are other tutorials on this subject,
    but I haven't found one on the GMC. Here goes...

    Understanding luminance is important when converting an image to grayscale. This is because grayscale conversion is accomplished by replacing a pixel's color with its luminance value. So what is luminance?
    According to google it is the intensity of light emitted from a surface per unit area in a given direction.
    So basically, luminance is the overall brightness of a color, without reference to the color's hue.

    The weighted vector that we will use for calculating luminance is:
    Code:
    const vec3 weight = vec3(0.2125,0.7154,0.0721);
    You may notice that this is green. This is because
    (according to Wikipedia) green light contributes the most to the intensity perceived by humans, and blue light the least.

    In Gm:Studio, finding a pixel's color can be done like so:
    Code:
    vec3 colors = texture2D(gm_BaseTexture,vTc).rgb; //Where vTc is the varying texture coordinates.
    Computing the luminance value is achieved by taking the dot product of the color vector with the weighted vector like this:

    Code:
    float luminance = dot(colors,weight);
    The only step that is left is to output the luminance value:
    Code:
    gl_FragColor=vec4(luminance,luminance,luminance,1.);
    If you want to retain the image's original alpha value, then you can store the value in a float:
    Code:
    float alpha=texture2D(gm_BaseTexture,vTc).a;
    Then the final output would be:
    Code:
    gl_FragColor=vec4(luminance,luminance,luminance,alpha);
    Boom. Grayscale shader!
    file0.jpg
    file1.jpg

    TL;DR
    attribute vec3 in_Position;
    attribute vec2 in_TextureCoord;
    varying vec2 vTc;
    void main()
    {
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]*vec4(in_Position.xyz,1.);
    vTc=in_TextureCoord;
    }
    varying vec2 vTc;
    void main()
    {
    const vec3 ww=vec3(0.2125,0.7154,0.0721);
    vec3 irgb=texture2D(gm_BaseTexture,vTc).rgb;
    float alpha=texture2D(gm_BaseTexture,vTc).a;
    float luminance=dot(irgb,ww);
    gl_FragColor=vec4(luminance,luminance,luminance,alpha);
    }
     
    Last edited by a moderator: Jul 6, 2016
  2. chance

    chance predictably random Forum Staff Moderator

    Joined:
    Apr 22, 2016
    Posts:
    798
    I agree there aren't many shader tutorials here, and I'd like to encourage more. So I accepted this one, even though it didn't follow the guidelines. (Plus, your kid is cute.)

    So I added the required information template, and put some of the code in codeboxes for clarity. Please try to use that format. It helps people get a quick sense of content when they're looking for particular subject matter.
     
    Noba and SoulTie like this.
  3. SoulTie

    SoulTie Guest

    I'm sorry chance! I am usually pretty good about reading the guidelines before posting. I will be sure to follow the rules next time!
     
  4. xygthop3

    xygthop3 Member

    Joined:
    Jun 20, 2016
    Posts:
    115
    You can improve the shader slightly and have only 1 texture fetch

    Code:
    attribute vec4 in_Position;
    attribute vec2 in_TextureCoord;
    varying vec2 vTc;
    void main() {
      gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
      vTc = in_TextureCoord;
    }
    
    Code:
    varying vec2 vTc;
    void main() {
      vec4 irgba=texture2D(gm_BaseTexture,vTc);
      float luminance=dot(irgba.rgb,vec3(0.2125,0.7154,0.0721));
      gl_FragColor=vec4(luminance,luminance,luminance,irgba.a);
    }
     
    Papa Doge and Roger Robertson like this.
  5. icuurd12b42

    icuurd12b42 TMC Founder GMC Elder

    Joined:
    Apr 22, 2016
    Posts:
    1,839
    nice! and a q for @xygthop3, would it not be more cost effective to const the grayscale vector outside of main than inside as in Soultie's example, or, in yours were at first glance one would think the vector would be constructed every pixel pass?
     
    SoulTie likes this.
  6. xygthop3

    xygthop3 Member

    Joined:
    Jun 20, 2016
    Posts:
    115
    Yup, it probably would be faster setting is as a const but you should do it outside the main function if you wanted to do it that way

    Code:
    varying vec2 vTc;
    
    const vec3 cgv = vec3(0.2125,0.7154,0.0721);
    
    void main()
    {
        vec4 irgba=texture2D(gm_BaseTexture,vTc);
        float luminance=dot(irgba.rgb, cgv);
        gl_FragColor=vec4(luminance,luminance,luminance,irgba.a);
    }
     
  7. SoulTie

    SoulTie Guest

    Now i'm all bashful :oops:
     
    Lonewolff likes this.
  8. SoulTie

    SoulTie Guest

    Thanks for the info! Using vec4's in the vertex and fragment shaders is a way better idea. Now I feel dumb. :rolleyes:
     
  9. SoulTie

    SoulTie Guest

    Yeah I probably should have done that. :p
     
  10. icuurd12b42

    icuurd12b42 TMC Founder GMC Elder

    Joined:
    Apr 22, 2016
    Posts:
    1,839
    Hehe Sorry to be hyper critical though. Shaders need to be as optimal as they can be. though I'm sure I have issues in my own shaders as well :)
     
    SoulTie likes this.
  11. SoulTie

    SoulTie Guest

    No worries, I need all the help I can get with shaders! This stuff is like black magic.
     
    Galladhan likes this.
  12. RichHopefulComposer

    RichHopefulComposer Member

    Joined:
    Jun 20, 2016
    Posts:
    1,384
    Thanks for the tutorial, soul. Not many shader tutorials around. Good work!
     
    SoulTie likes this.
  13. BlueBird02

    BlueBird02 Member

    Joined:
    Jul 25, 2016
    Posts:
    52
    Is there a picture you can give an example?
     
  14. Aura

    Aura Guest

    They have already given one inside the spoilers in the OP.

    I'll repost them here:

    Colour (before applying the shader)

    View attachment 907

    Grayscale (after applying the shader)

    View attachment 908
     
  15. Ryan Geraldo

    Ryan Geraldo Guest

    Nice tutorial - thanks for sharing!;)
     
  16. Galladhan

    Galladhan Member

    Joined:
    Jul 1, 2016
    Posts:
    119
    LOL! I have the same feeling.
     
  17. Ampersand

    Ampersand Guest

    So I've been playing with shaders and I'm beginning to grasp the markup, the terminology, and a lot of basic manipulations and transformations. However, I can't seem to figure out how I can use a shader in a varying way. For instance, if I wanted what I'm drawing to transition from passthrough to this greyscale shader, how could I use uniforms to pass a variable for a ratio of "how greyscale" it should be? I know it's probably not hard, I'm just having trouble finding information on more advanced shader techniques in GM:S. Thanks!
     
  18. xygthop3

    xygthop3 Member

    Joined:
    Jun 20, 2016
    Posts:
    115
    The greyscale shader here https://marketplace.yoyogames.com/assets/261/free-shaders shows how to pass a uniform which allows adjustment of the colour ratio.
     
    Ampersand likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice