user2145312
user2145312

Reputation: 928

libGDX Grayscale Shader fade effect

I am using a shader I found provided in another stack overflow question to render my screen in grayscale:

import com.badlogic.gdx.graphics.glutils.ShaderProgram;

public class GrayscaleShader {
static String vertexShader = "attribute vec4 a_position;\n" +
        "attribute vec4 a_color;\n" +
        "attribute vec2 a_texCoord0;\n" +
        "\n" +
        "uniform mat4 u_projTrans;\n" +
        "\n" +
        "varying vec4 v_color;\n" +
        "varying vec2 v_texCoords;\n" +
        "\n" +
        "void main() {\n" +
        "    v_color = a_color;\n" +
        "    v_texCoords = a_texCoord0;\n" +
        "    gl_Position = u_projTrans * a_position;\n" +
        "}";

static String fragmentShader = "#ifdef GL_ES\n" +
        "    precision mediump float;\n" +
        "#endif\n" +
        "\n" +
        "varying vec4 v_color;\n" +
        "varying vec2 v_texCoords;\n" +
        "uniform sampler2D u_texture;\n" +
        "\n" +
        "void main() {\n" +
        "  vec4 c = v_color * texture2D(u_texture, v_texCoords);\n" +
        "  float grey = (c.r + c.g + c.b) / 3.0;\n" +
        "  gl_FragColor = vec4(grey, grey, grey, c.a);\n" +
        "}";

public static ShaderProgram grayscaleShader = new ShaderProgram(vertexShader,
        fragmentShader);
}

I would like to be able to fade to grayscale from full color. I'm imagining I should be able to do this by tweening the r,g and b colors which are then set to gl_FragColor (im imagining i will need to create a new ShaderProgram in each setter method used in the tweening process and just use the new color value each time). I understand why (c.r + c.g + c.b)/3.0 is gray, but what can I use to get the values from color to this point? I hope this makes sense!

Upvotes: 2

Views: 981

Answers (1)

Tenfour04
Tenfour04

Reputation: 93601

You can put a uniform float in your fragment shader that blends between the original color and gray like this:

static final String FRAG = "#ifdef GL_ES\n" +
        "    precision mediump float;\n" +
        "#endif\n" +
        "\n" +
        "varying vec4 v_color;\n" +
        "varying vec2 v_texCoords;\n" +
        "uniform sampler2D u_texture;\n" +
        "uniform float u_grayness;\n" +
        "\n" +
        "void main() {\n" +
        "  vec4 c = v_color * texture2D(u_texture, v_texCoords);\n" +
        "  float grey = (c.r + c.g + c.b) / 3.0;\n" +
        "  vec3 blendedColor = mix(c.rgb, vec3(grey), u_grayness);\n" +
        "  gl_FragColor = vec4(blendedColor.rgb, c.a);\n" +
        "}";

You can keep a float that tracks the "grayness" from 0 to 1 and apply it like this:

batch.setShader(grayscaleShader);
batch.begin();
grayscaleShader.setUniformf("u_grayness", grayness);
//draw stuff
batch.end();
batch.setShader(null);

By the way, you might have better results by using a proper luminance scale for calculating gray, instead of just summing and dividing by 3. This is because red, green, and blue have different relative luminance as perceived by the eye. Example:

float grey = dot( c.rgb, vec3(0.22, 0.707, 0.071) );

Upvotes: 6

Related Questions