Search code examples
javascriptthree.jsglslshadervertex-shader

How to scale a texture on a Texture Atlas while maintaining its offset?


How can I scale a specific texture on a texture atlas in GLSL while ignoring the other textures? For example, let's say I have a texture Atlas that has 16 textures.

enter image description here

I found the texture I need using this formula:

float scale = 1.0/0.25;
vec2 idx = vec2(1.0,2.0);
vec2 newUvForTexture = (vUv+idx)*scale;
vec4 tex = texture2D(texArray[0],newUvForTexture);

The texture at (1.0,2.0):

enter image description here

Once I have the appropriate UV (newUvForTexture give me the image above).But what if I need to scale it down, something like this newUvForTexture*2.0 while maintaining its offset? Can someone provide a GLSL code example of how to accomplish this?

HERE IS A WORKING EXAMPLE: https://codepen.io/miguel007/pen/VwGpjvm

Again this is what I would like to accomplish, is to scale it up or down while keeping the offset and no other textures appearing:

      float scale   = 1.0/4.0;
      vec2 offset   = vec2(1.0,2.0);
      vec2 newUv    = (vUv+offset)*scale;
      gl_FragColor  = vec4(texture2D(u_tex,newUv).rgb,1.0);
      /* 
      vec2 scaledUp = newUv*.8;
      gl_FragColor  = vec4(texture2D(u_tex,scaledUp).rgb,1.0);
      or 
      vec2 scaledDown = newUv*2.0;
      gl_FragColor    = vec4(texture2D(u_tex,scaledDown).rgb,1.0);
      */

Solution

  • You can use the clamp() glsl function to stop the UVs from progressing beyond the desired min/max values:

    float scale = 1.0/0.25;
    vec2 idx = vec2(1.0,2.0);
    vec2 newUvForTexture = (vUv+idx) * scale;
    
    // Get minimum & maximum limits of UVs
    vec2 min = vec2(idx.x + 0.0, idx.y + 0.0) * scale;
    vec2 max = vec2(idx.x + 1.0, idx.y + 1.0) * scale;
    
    // Limit the range of UVs with clamp()
    vec2 clampedUVs = clamp(newUvForTexture, min, max);
    
    // Sample texture with clamped UVs.
    vec4 tex = texture2D(texArray[0], clampedUVs);
    

    You could probably optimize this further by performing the * scale multiplication only once, but this way it stays closest to your original code for clarity.

    Here's a working demo using the above approach: https://codepen.io/marquizzo/pen/zYJwGMy