1
\$\begingroup\$

I'm using a basic pixel shader here

uniform sampler2D texture;
uniform float pixel_threshold;

void main()
{
    float factor = 1.0 / (pixel_threshold + 0.001);
    vec2 pos = floor(gl_TexCoord[0].xy * factor + 0.5) / factor;
    gl_FragColor = texture2D(texture, pos) * gl_Color;
}

It works great in my game. But on a new enemy it doesn't. This enemy is different because it uses a texture atlas- one big texture holding more frames than just the character. The sampling gets messed up because factorapproaches 0 so does the xy coord therefore sampling some pixels outside of the character's sheet. Likewise when pixel_threshold gets small, the xy coord goes outside of the character's spot in the texture.

Clamping is not the solution because we want it to wrap around. I've tried many ways of wrapping but not seem to offer the desired effect of the original.

I tried using 4 uniforms: left,top,width,height and passing in the masking values used when drawing the sprite from the spritesheet.

vector2 size = mySprite.getSize();
float left = mySprite.getTextureRect().left / size.x;   
float top  = mySprite.getTextureRect().top / size.y;
float width= mySprite.getTextureRect().width/ size.x;
float height=mySprite.getTextureRect().height/size.y;

myShader.setUniformf("left", left);
...
myShader.setUniformf("height", height);

How can I have the pixel blur shader stay within the texel ranges provided by the uniform? Thanks.

\$\endgroup\$
  • \$\begingroup\$ Note that if you need to set distinct uniforms for each character that needs to wrap across a different subset of the atlas, you lose the batching benefits that the atlas would otherwise have provided. You might be better off adding extra padding so you don't need special wrapping logic for each part (while keeping batching), or skipping the atlas and drawing the characters as individual textures so your wrapping works natively. \$\endgroup\$ – DMGregory Jul 12 '18 at 1:10
  • \$\begingroup\$ but that is not what I want to achieve \$\endgroup\$ – Dan Jul 12 '18 at 2:32
1
\$\begingroup\$

I would do this by converting the texture coordinates to "sprite-normalized" coordinates, do your math on them, and then convert back to atlas coordinates. You'd do this by doing something like the following:

// Get the texture coordinate
vec2 texCoord = gl_TexCoord[0].xy;

// Make its lower-left be at (0,0) and it's upper right be at (1,1)
texCoord = (texCoord - origin) / size;

// Apply the factor
float factor = 1.0 / (pixel_threshold + 0.001);
texCoord = floor(texCoord * factor + 0.5) / factor;

// Apply the wrapping
texCoord = fract(texCoord);

// Convert back to texture atlas coordinates
texCoord = (texCoord * size) + origin;

In this case origin is the (left, bottom) you calculated in your code above and size is the (width, height) you calculated.

\$\endgroup\$
  • \$\begingroup\$ You did it! It looks exactly the way it should now. Brilliant. \$\endgroup\$ – Dan Jul 12 '18 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.