Graphics GrayScale Shader

S

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:

chance

predictably random
Forum Staff
Moderator
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.
 
S

SoulTie

Guest
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.)
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!
 

xygthop3

Member
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);
}
 
I

icuurd12b42

Guest
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?
 

xygthop3

Member
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);
}
 
I

icuurd12b42

Guest
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 :)
 
S

SoulTie

Guest
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 :)
No worries, I need all the help I can get with shaders! This stuff is like black magic.
 
A

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!
 

xygthop3

Member
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!
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.
 
Top