1
votes

I'm programing a GUI library in openGL and decided to add rounded corners because I feel like it gives a much more professional look to the units. I've implemented the common

length(max(abs(p) - b, 0.0)) - radius

method and it almost works perfectly except for the fact tat the corners seems as though they are stretched:

enter image description here

My fragment shader:

in vec2 passTexCoords;

uniform vec4 color;
uniform int width;
uniform int height;
uniform int radius;    

void main() {
    fragment = color;

    vec2 pos = (abs(passTexCoords - 0.5) + 0.5) * vec2(width, height);

    float alpha = 1.0 - clamp(length(max(pos - (vec2(width, height) - radius), 0.0)) - radius, 0.0, 1.0);

    fragment.a = alpha;
}

The stretching does make sense to me but when I replace with

vec2 pos = (abs(passTexCoords - 0.5) + 0.5) * vec2(width, height) * vec2(scaleX, scaleY);

and

float alpha = 1.0 - clamp(length(max(pos - (vec2(width, height) * vec2(scaleX, scaleY) - radius), 0.0)) - radius, 0.0, 1.0);

(where scaleX and scaleY are scalars between 0.0 and 1.0 that represent the width and height of the rectangle relative to the screen) the rectangle almost completely disappears:

enter image description here

2
The first thing you need to do is to figure out (and mark with a comment) which coordinate system each of your vectors is supposed to be in. - Magma
Camera coordinates? Window pixel coordinates? Or are you using a custom coordinate system? - Magma
Alright. passTexCoord is just the corners of the rectangle in texel space (0.0 - 1.0). width and height are the width and height of the screen in pixels. radius is also in pixels - J. Lengel

2 Answers

1
votes

The problem is that the distances are not scaled into screen space, and are therefore stretched across the greatest window axis as a result. You can fix this if you multiply the normalized position by the aspect ratio of the screen, along with the other parameters for the box. I wrote an example on Shadertoy that does this:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Input info
    vec2 boxPos; // The position of the center of the box (in normalized coordinates)
    vec2 boxBnd; // The half-bounds (radii) of the box (in normalzied coordinates)
    float radius;// Radius


    boxPos = vec2(0.5, 0.5);    // center of the screen
    boxBnd = vec2(0.25, 0.25);  // half of the area
    radius = 0.1;

    // Normalize the pixel coordinates (this is "passTexCoords" in your case)
    vec2 uv = fragCoord/iResolution.xy;

    // (Note: iResolution.xy holds the x and y dimensions of the window in pixels)
    vec2 aspectRatio = vec2(iResolution.x/iResolution.y, 1.0);

    // In order to make sure visual distances are preserved, we multiply everything by aspectRatio
    uv *= aspectRatio;
    boxPos *= aspectRatio;
    boxBnd *= aspectRatio;

    // Time varying pixel color
    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen
    float alpha = length(max(abs(uv - boxPos) - boxBnd, 0.0)) - radius;

    // Shadertoy doesn't have an alpha in this case
    if(alpha <= 0.0){
        fragColor = vec4(col,1.0);
    }else{
        fragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
}

There may be a less computationally expensive way to do this, but this was a simple solution I cooked up.

0
votes

I assume the passTexCoords is a a texture coordinate in range [0, 1]. And that width and height is the size of the screen. And scaleX and scaleY is the ration of the green area to the size of the screen.
Calculate the absolute position (pos) of the current fragment in relation to the center of the green area in pixel units:

vec2 pos = (abs(passTexCoords - 0.5) + 0.5) * vec2(width*scaleX, height*scaleY);

Calculate the distance from the center point of the arc, to the current fragment:

vec2 arc_cpt_vec = max(pos - vec2(width*scaleX, height*scaleY) + radius, 0.0);

If the length of the vector is greater than the radius, then the fragment has to be skipped:

float alpha = length(arc_cpt_vec) > radius ? 0.0 : 1.0;