Search code examples
opengllibgdxglslshaderfragment-shader

Failed attempt to create a pixelating shader for opengl


I'm making a libGDX based game and I've tried to make a pixelating shader. To me, it looks like it should work but it doesn't. I just see 1 color of the texture all over the screen. The goal was to turn a detailed texture into a pixelated texture. Here is the code of my fragment shader:

precision mediump float;
varying vec4 v_color;
varying vec2 v_texCoord0;
uniform sampler2D u_sampler2D;
void main(){
    ivec2 texSize = textureSize( u_sampler2D, 0);
    vec4 color = texture2D(u_sampler2D, vec2(int(v_texCoord0.x * texSize.x) / texSize.x, int(v_texCoord0.y * texSize.y) / texSize.y)) * v_color;
    gl_FragColor = color;
}

What I am trying to do here is: I get the size of the texture. Then, with that size, I 'pixelate' v_texCoord0 and get the color of that pixel.

As soon as I remove the int cast in

int(v_texCoord0.x * texSize.x) / texSize.x, int(v_texCoord0.y * texSize.y)

, I see the texture as normal, otherwise I see what I've described in the beginning of this post. However, to me, anything in my code could be wrong.

I hope someone with experience could help me fix this problem!


Solution

  • You are doing an integer division here:

    ivec2 texSize;
    [...] int(v_texCoord0.x * texSize.x) / texSize.x
    

    The result can only be an integer, and if v_texCoord0.x is in the range [0,1], it will result in producing zero except for rightmost part of your texture, when the fragment exacly samples at the border of your texture.

    You should apply floating-point math to get the effect you want:

    vec2 texSize=vec2(textureSize( u_sampler2D, 0));
    vec4 color = texture2D(u_sampler2D, floor(v_texCoord0 * texSize) / texSize);
    

    (Also note that there is no need to work with the x and y components individually, you can use vector operations.)

    But what you're doing here is not completely clear. Concenptually, you are emulating what GL_NEAREST filtering can do for you for free (just that your selection is shifted by half a texel), so the question is: what are you getting from this. If you use GL_LINEAR filtering, the above formula will sample always at the texel corners, so that the linear filter will result in averaging the color of a 2x2 texel block. If you use GL_NEAREST, the formula will not give you more pixelation than you had before, it just shifts the texture in a weird way. If you use some filter with mipmapping, the formula will completely break the mipmap selection due to the non-continuous nature of the equation (this will also result in the GL not being able to discern between texture minification or magnification in a reliable way, so it does not break only mipmapping).