I have some sprites in my game, mostly though not entirely UI related, that I would like to be able to scale to different sizes without warping the borders of the sprite.
Looking around I was able to find the following two threads
https://gamedev.stackexchange.com/questions/153848/how-do-i-set-up-9-slicing-in-opengl
https://www.gamedev.net/forums/topic/696879-glsl-9-slicing/
And from those, was able to get the following fragment shader
#version 330 core
in vec2 TextureCoordinates;
uniform sampler2D image;
uniform vec2 u_dimensions;
uniform vec2 u_border;
uniform vec4 spriteColor;
uniform vec4 bounds;
float map(float value, float originalMin, float originalMax, float newMin, float newMax) {
return (value - originalMin) / (originalMax - originalMin) * (newMax - newMin) + newMin;
}
// Helper function, because WET code is bad code
// Takes in the coordinate on the current axis and the borders
float processAxis(float coord, float textureBorder, float windowBorder)
{
if (coord < windowBorder)
return map(coord, 0, windowBorder, 0, textureBorder) ;
if (coord < 1 - windowBorder)
return map(coord, windowBorder, 1 - windowBorder, textureBorder, 1 - textureBorder);
return map(coord, 1 - windowBorder, 1, 1 - textureBorder, 1);
}
vec2 subspriteMap(vec2 inner)
{
return mix(vec2(bounds.x, bounds.z), vec2(bounds.y, bounds.w), inner.xy);
}
void main(void)
{
vec2 newUV = vec2(
processAxis(TextureCoordinates.x, u_border.x, u_dimensions.x),
processAxis(TextureCoordinates.y, u_border.y, u_dimensions.y)
);
newUV = subspriteMap(newUV);
gl_FragColor = vec4(spriteColor) * texture2D(image, newUV);
}
Where u_dimensions is the border size divided by what I am scaling the sprite to, u_border is the border size divided by the original size of the sprite, and bounds are the uv coordinates of the sprite within the sprite sheet.
This works perfectly fine when the interior of my textures is featureless, and allows those textures to be part of a larger sprite sheet/atlas.
However, if I want to have some sort of pattern in the interior, that is still stretched out.
With some additional research, I made the following changes
float map(float val, float oMin, float oMax, float nMin, float nMax) {
return (val - oMin) / (oMax - oMin) * (nMax - nMin) + nMin;
}
float mapInside(float val, float min, float innerStart, float innerSize, float wBorder, float tBorder) {
float scale = (1.0 - wBorder * 2.0) / (1.0 - tBorder * 2.0);
return mod((val - min) * scale, innerSize) + innerStart;
}
float processAxis(float val, float tBorder, float wBorder) {
if (val < wBorder) { // check if in near border
return map(val, 0.0, wBorder, 0.0, tBorder);
}
float farEdge = 1.0 - wBorder;
if (val >= farEdge) { // check if in far border
return map(val, farEdge, 1.0, 1.0 - tBorder, 1.0);
}
// must be in inside section of sprite
return mapInside(val, wBorder, tBorder, 1.0 - tBorder - tBorder, wBorder, tBorder);
}
And with that, I get the following result
Which is still not what I want. I want something closer to this, which I created manually
What I would like is for the interior of the pattern to repeat, similar to an example image of something that one can do in Gamemaker, and indeed most engines that I have seen.
While it seems like it is just a simple option in most game engines, I am at a loss for how I could set it up myself. Any ideas? Using GL_REPEAT as a texture parameter doesn't really work, as the sprites I am using are part of a sheet, and I don't want to repeat most of them, just the interiors of these ones.