How do I keep texture size and aspect ratio while applying rounded corners?

I’m trying to apply rounded corners to a texture, while keeping the texture’s size to be exactly half that of the ThreeJS plane it is being drawn on. As well as keeping the aspect ratio of the image, and not stretching the rounded corners. I’ve gotten pretty close to what I want, but as you can see in the screenshot here, the texture is not quite filling the black area. It should not have the extra black space on the left and right sides of the texture.

In this screenshot, the texture is filling the area the way I’d like, but the rounded corners are being stretched:

Here’s my fragment shader and a shadertoy: Shader - Shadertoy BETA

float roundedRectangle (vec2 uv, vec2 pos, vec2 size, float radius, float thickness)
{
  float d = length(max(abs(uv - pos),size) - size) - radius;
  return smoothstep(0.66, 0.33, d / thickness * 5.0);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    float aspect = iResolution.x / iResolution.y;
    
    vec2 center = vec2(.5);
    
    vec2 texCoord = (uv - .25) * 2.;//scale texture to be half the size of three.js plane
    
    //This doesn't quite fill the area correctly, but the rounded corners don't get stretched:
    float rounded = roundedRectangle(vec2(uv.x * aspect, uv.y), vec2(center.x * aspect, center.y), vec2(.2 * aspect,.2), 0.05, 0.05);
    
    //This fills the area correctly, but the rounded corners get stretched:
    //float rounded = roundedRectangle(vec2(uv.x, uv.y), vec2(center.x, center.y), vec2(.2,.2), 0.05, 0.05);

    vec4 col = texture(iChannel0, texCoord) * rounded;
   
    if (uv.x > .25 && uv.x < .75 && uv.y > .25 && uv.y < .75) {
        fragColor = col;
    } else {
    
        //notice the black space left around the texture
        fragColor = vec4(1., 0., 0., 0.);
    }
}

If context is helpful:
In the end, I should be able to apply this as a ShaderMaterial to dom image elements with various sizes and aspect ratios. I’m creating a ThreeJS plane for each dom image element that is exactly twice as big as the image element so that I can do some other stuff “outside” of the image element with the shader. That’s why I’m scaling the texture down to half size and placing it in the middle of the material.

Any help would be appreciated! Thanks!

you need alpha blend mode in your shader and then sample the mask values for the alpha for a lerp on the texture.
Or if you just want the red, lerp that instead of alpha
The system is, you have to draw something at that pixel, and if you assign nothing its gonna default to whatever you sent to the fragcolor and that would be black so its draws 0

this is the red lerp, they call it mix in shadertoy

fragColor = mix(vec4(1.0,0,0,0), col, rounded);

float roundedRectangle (vec2 uv, vec2 pos, vec2 size, float radius, float thickness)
{
  float d = length(max(abs(uv - pos),size) - size) - radius;
  return smoothstep(0.66, 0.33, d / thickness * 5.0);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    float aspect = iResolution.x / iResolution.y;
    
    vec2 center = vec2(.5);
    
    vec2 texCoord = (uv - .25) * 2.;//scale texture to be half the size of three.js plane
    
    float rounded = roundedRectangle(vec2(uv.x * aspect, uv.y), vec2(center.x * aspect, center.y), vec2(.2 * aspect,.2), 0.05, 0.05);
 
 
 
    // just these parts changed
    vec4 col = texture(iChannel0, texCoord);
   
    fragColor = mix(vec4(1.0,0,0,0), col, rounded);

}

Thanks for the response! It looks like your solution gets rid of the black, but the texture is still the same width and is getting a little cut off on the left and right side:

The black and red is just kind of there to show the problem. I’m trying to have the texture be the same width as the black area in the first image in my first post (like it is in the second image), but without stretching the rounded corners.

Hey there, I whipped up a quick & dirty alternate version of roundedRectangle(), let me know if this works for you:

float roundedRectangle (float aspect, vec2 uv, vec2 pos, vec2 size, float radius, float thickness)
{
  vec2 halfSize = size * 0.5;
  vec2 posToUV = uv - pos;
  
  // size of the image cropped by the radius
  vec2 croppedSize = vec2(halfSize) - vec2(radius / aspect, radius);

  vec2 fromCorner = halfSize - abs(posToUV);
  fromCorner.x *= aspect;
  float d1 = length(fromCorner);
  
  vec2 fromCropped = croppedSize - abs(posToUV);
  fromCropped.x *= aspect;
  float d2 = length(fromCropped);
  
  float aaFromRadius = radius * (1.0 - thickness);
  float aaToRadius = radius * (1.0 + thickness);
  return step(abs(posToUV.y), halfSize.y) *
         step(abs(posToUV.x), halfSize.x) *
        (1.0 - ((1.0 - smoothstep(aaFromRadius, aaToRadius, d1)) *
        smoothstep(aaFromRadius, aaToRadius, d2)));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    float aspect = iResolution.x / iResolution.y;
    
    vec2 center = vec2(.5);
    
    float imageScale = 0.5;
    vec2 texCoord = (uv - center) / imageScale + center;
    
    float rounded = roundedRectangle(aspect, vec2(uv.x, uv.y), vec2(center.x, center.y), vec2(1.0, 1.0) * imageScale, 0.05, 0.05);

    vec4 col = texture(iChannel0, texCoord) * rounded;
   
    if (uv.x > center.x - imageScale * 0.5 && uv.x < center.x + imageScale * 0.5 && uv.y > center.y - imageScale * 0.5 && uv.y < center.y + imageScale * 0.5) {
        fragColor = col;
    } else {
        fragColor = vec4(1., 0., 0., 0.);
    }
}

(I couldn’t quite make sense of the previous version :slight_smile: )

1 Like

just as an additional node on your journey into shaders. Dont think of shaders as colors but as values. You have your color 3 sets of 0-1 values in a vec3 and you have a mask 0-1
Everything you do in shaders to mashing those two together to get a color out.
And you will use lerp (mix) and smoothstep() in like EVERYTHING more complex than texture color get to know these two functions at the mathematical level

go though ALL of https://thebookofshaders.com to really “get” it
Its fun!!

1 Like

Cool, it looks like that updated roundedRectangle function does the trick! It’s also a lot easier to control the size of the corners. I appreciate it! And I’ve been going through The Book of Shaders, it is super helpful! Thanks for the advice.