GLSL 圆角矩形角被拉伸

GLSL Rounded Rectangle Corners Are Stretched

我正在用 openGL 编写一个 GUI 库,并决定添加圆角,因为我觉得它可以使单元看起来更专业。 我已经实施了常见的

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

方法,它几乎完美地工作,除了角落看起来好像被拉伸的事实:

我的片段着色器:

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;
}

拉伸对我来说确实有意义,但是当我替换为

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

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

(其中 scaleX 和 scaleY 是介于 0.0 和 1.0 之间的标量,表示矩形相对于屏幕的宽度和高度)矩形几乎完全消失:

我假设 passTexCoords 是 [0, 1] 范围内的纹理坐标。而那个widthheight就是屏幕的大小。而scaleXscaleY是绿色区域占屏幕大小的比例。
以像素为单位计算当前片段相对于绿色区域中心的绝对位置(pos):

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

计算圆弧中心点到当前片段的距离:

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

如果向量的长度大于半径,则必须跳过片段:

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

问题是距离没有按比例缩放到屏幕 space 中,因此在最大的 window 轴上被拉伸。如果将归一化位置乘以屏幕的纵横比以及框的其他参数,则可以解决此问题。我在 Shadertoy 上写了一个例子:

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);
    }
}

可能有一种计算成本较低的方法可以做到这一点,但这是我想出的一个简单的解决方案。