Texture grayscale in libgdx
Asked Answered
R

3

7

Is there a way to transform a libgdx's Texture to a grayscale image? So far I had duplicate the images that I want to grayscale and I did it manually, but I think it is not the best solution because my game is using more and more images and it uses a lot of disk space.

Rosinarosinante answered 7/7, 2013 at 20:40 Comment(0)
N
7

You should be able to write a GLSL shader that renders a texture in grayscale. This requires OpenGL 2.x, and doesn't really "transform" a texture, but just renders it to the display as grayscale.

For a detailed tutorial on shaders that includes a grayscale shader check out: https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson3

(Libgdx doesn't really define the GLSL shader API, that's passed through from OpenGL, so most tutorials or code you find on the web for regular OpenGL should work.)

For a more direct hack, just take the Libgdx SpriteBatch shader and change the fragment shader so it averages the rgb components. (You can define your own ShaderProgram and provide it to a SpriteBatch to use.) Change body of the fragment shader to something like this (untested, so may not compile):

+ "  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" //
Noriega answered 7/7, 2013 at 23:50 Comment(0)
A
10

Thought I'd share this for anyone wanting to use some copy/paste code.

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

To use it call

spriteBatch.setShader(GrayscaleShader.grayscaleShader)

And when you're done with grayscale don't forget to call

spriteBatch.setShader(null);
Anticyclone answered 7/4, 2014 at 11:45 Comment(2)
You should really put shaders in a seperate file and load them in (and use some neat shader tools), if you want a maintainability readability nightmare choose this option.Magdamagdaia
@iLoveUnicorns Agreed. But that's not a copy/paste option for someone wanting a quick solution to play with and test. This doesn't prevent them from doing that, it's just a step in the right direction.Anticyclone
N
7

You should be able to write a GLSL shader that renders a texture in grayscale. This requires OpenGL 2.x, and doesn't really "transform" a texture, but just renders it to the display as grayscale.

For a detailed tutorial on shaders that includes a grayscale shader check out: https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson3

(Libgdx doesn't really define the GLSL shader API, that's passed through from OpenGL, so most tutorials or code you find on the web for regular OpenGL should work.)

For a more direct hack, just take the Libgdx SpriteBatch shader and change the fragment shader so it averages the rgb components. (You can define your own ShaderProgram and provide it to a SpriteBatch to use.) Change body of the fragment shader to something like this (untested, so may not compile):

+ "  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" //
Noriega answered 7/7, 2013 at 23:50 Comment(0)
A
2

You can load up textures as luminance only, or luminance and alpha in GLES (see glTexImage2D). In libgdx you can specify PixFormat.Intensity (luminance) or LuminanceAlpha (luminance and alpha) when instantiating the Texture. This will generate a grayscale texture.

You still need to have two textures (one color, one grayscale) loaded up, but they can use the same source, and the luminance only uses only 1 byte per pixel in memory. A more efficient solution is to implement a shader as suggested by P.T., but is only available from GLES 2.

Ahoufe answered 8/7, 2013 at 2:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.