#version 440 layout(location = 0) in vec2 qt_TexCoord0; layout(location = 0) out vec4 fragColor; layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; float paddedX; float paddedY; float paddedW; float paddedH; float smoothFactor; int rectCount; int myIndex; vec4 color; int hasInverted; float invertedRadius; vec4 invertedOuter; vec4 invertedInner; vec4 rectData[80]; float shadowRadius; vec4 shadowColor; vec4 invertedOuterRadii; }; float sdRoundedBox(vec2 p, vec2 center, vec2 halfSize, float radius) { vec2 d = abs(p - center) - halfSize + vec2(radius); return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0) - radius; } float sdRoundedBox4(vec2 p, vec2 center, vec2 halfSize, vec4 r) { p -= center; r.xy = (p.x > 0.0) ? r.xy : r.wz; r.x = (p.y > 0.0) ? r.y : r.x; vec2 q = abs(p) - halfSize + r.x; return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x; } float sdBox(vec2 p, vec2 center, vec2 halfSize) { vec2 d = abs(p - center) - halfSize; return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0); } float smin(float a, float b, float k) { float h = max(k - abs(a - b), 0.0) / k; return min(a, b) - h * h * h * k * (1.0 / 6.0); } float smax(float a, float b, float k) { float h = max(k - abs(a - b), 0.0) / k; return max(a, b) + h * h * h * k * (1.0 / 6.0); } float smaxSharpA(float a, float b, float k) { float h = max(k - abs(a - b), 0.0) / k; float blend = h * h * h * k * (1.0 / 6.0); blend *= smoothstep(0.0, k * 0.5, -a); return max(a, b) + blend; } void main() { vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH); float dArr[16]; int owner = -2; float minDist = 1e10; for (int i = 0; i < rectCount; i++) { vec4 rect = rectData[i * 5]; // cx, cy, hw, hh vec4 props = rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0 vec4 radii = rectData[i * 5 + 4]; // effective per-corner radii (tr, br, bl, tl) vec2 center = rect.xy + props.yz; vec2 extent = sh.xy + vec2(smoothFactor * 1.5 + shadowRadius); if (abs(pixel.x - center.x) > extent.x || abs(pixel.y - center.y) > extent.y) { dArr[i] = 1e10; continue; } mat2 invDeform = mat2(invDm.xy, invDm.zw); vec2 transformedPixel = center + invDeform * (pixel - center); float d = sdRoundedBox4(transformedPixel, center, rect.zw, radii); d *= max(props.w, 0.01); if (hasInverted != 0) { vec2 screenHalf = sh.xy; float distY0 = (center.y + screenHalf.y) - (invertedInner.y - invertedInner.w); float distY1 = (invertedInner.y + invertedInner.w) - (center.y - screenHalf.y); float distX0 = (center.x + screenHalf.x) - (invertedInner.x - invertedInner.z); float distX1 = (invertedInner.x + invertedInner.z) - (center.x - screenHalf.x); float yProx = 1.0 - min(smoothstep(0.0, smoothFactor, distY0), smoothstep(0.0, smoothFactor, distY1)); float xProx = 1.0 - min(smoothstep(0.0, smoothFactor, distX0), smoothstep(0.0, smoothFactor, distX1)); vec2 q = abs(pixel - center) - screenHalf; vec2 qp = max(q, vec2(0.0)); float cornerLen = length(qp); float gradX = qp.x / max(cornerLen, 0.001); float gradY = qp.y / max(cornerLen, 0.001); float faceY = smoothstep(-4.0, 4.0, q.y - q.x); float faceX = 1.0 - faceY; float t = smoothstep(0.0, 2.0, cornerLen); float xWeight = mix(faceX, gradX, t); float yWeight = mix(faceY, gradY, t); float boost = 3.0; float scale = 1.0 + (xProx * xWeight + yProx * yWeight) * boost; d *= scale; } dArr[i] = d; if (d < smoothFactor && d < minDist) { minDist = d; owner = i; } } float mergedSdf = 1e10; for (int i = 0; i < rectCount; i++) { mergedSdf = min(mergedSdf, dArr[i]); } for (int i = 0; i < rectCount; i++) { if (dArr[i] >= 1e9) continue; int excludeMask = floatBitsToInt(rectData[i * 5 + 1].x); for (int j = i + 1; j < rectCount; j++) { if (dArr[j] >= 1e9) continue; if ((excludeMask & (1 << j)) != 0) continue; if (abs(dArr[i] - dArr[j]) >= smoothFactor) continue; mergedSdf = min(mergedSdf, smin(dArr[i], dArr[j], smoothFactor)); } } if (hasInverted != 0) { float dOuter = sdRoundedBox4(pixel, invertedOuter.xy, invertedOuter.zw, invertedOuterRadii) - 1.0; float dInner = sdRoundedBox(pixel, invertedInner.xy, invertedInner.zw, invertedRadius); float innerTop = invertedInner.y - invertedInner.w; float innerBot = invertedInner.y + invertedInner.w; float innerLeft = invertedInner.x - invertedInner.z; float innerRight = invertedInner.x + invertedInner.z; float outerTop = invertedOuter.y - invertedOuter.w; float outerBot = invertedOuter.y + invertedOuter.w; float outerLeft = invertedOuter.x - invertedOuter.z; float outerRight = invertedOuter.x + invertedOuter.z; float topZone = 1.0 - smoothstep(innerTop, innerTop + smoothFactor, pixel.y); float botZone = smoothstep(innerBot - smoothFactor, innerBot, pixel.y); float leftZone = 1.0 - smoothstep(innerLeft, innerLeft + smoothFactor, pixel.x); float rightZone = smoothstep(innerRight - smoothFactor, innerRight, pixel.x); float sinkValue = 0.0; float preOff = smoothFactor * (1.0 / 6.0); float s = smoothFactor * 2.0; if (topZone > 0.0 || botZone > 0.0 || leftZone > 0.0 || rightZone > 0.0) { for (int i = 0; i < rectCount; i++) { vec4 rect = rectData[i * 5]; vec4 sinkProps = rectData[i * 5 + 1]; vec2 sinkSh = rectData[i * 5 + 3].xy; vec2 ctr = rect.xy + sinkProps.yz; float topPen = clamp(innerTop - (ctr.y + sinkSh.y) - preOff, 0.0, innerTop - outerTop); float botPen = clamp((ctr.y - sinkSh.y) - innerBot - preOff, 0.0, outerBot - innerBot); float leftPen = clamp(innerLeft - (ctr.x + sinkSh.x) - preOff, 0.0, innerLeft - outerLeft); float rightPen = clamp((ctr.x - sinkSh.x) - innerRight - preOff, 0.0, outerRight - innerRight); float hLat = max(abs(pixel.x - ctr.x) - sinkSh.x, 0.0); float vLat = max(abs(pixel.y - ctr.y) - sinkSh.y, 0.0); float sink = max(max(topPen * smoothstep(s, 0.0, hLat) * topZone, botPen * smoothstep(s, 0.0, hLat) * botZone), max(leftPen * smoothstep(s, 0.0, vLat) * leftZone, rightPen * smoothstep(s, 0.0, vLat) * rightZone)); sinkValue = max(sinkValue, sink); } } dInner -= sinkValue; float negInner = -dInner; float dFrame = dOuter >= negInner ? dOuter : smaxSharpA(dOuter, negInner, smoothFactor); mergedSdf = smin(mergedSdf, dFrame, smoothFactor); if (dFrame < minDist) { owner = -1; } } float reach = max(smoothFactor, shadowRadius); float pxWidth = max(length(vec2(dFdx(mergedSdf), dFdy(mergedSdf))), 1e-5); if (mergedSdf > reach - max(2.0 * pxWidth, 1.0)) discard; if (myIndex >= -1 && owner != myIndex) discard; float materialAlpha = clamp(0.5 - mergedSdf / pxWidth, 0.0, 1.0); float shadowAlpha = 0.0; if (shadowRadius > 0.0 && myIndex == -2) { float t = clamp(mergedSdf / shadowRadius, 0.0, 1.0); shadowAlpha = (1.0 - t) * (1.0 - t) * shadowColor.a; } float totalAlpha = materialAlpha + shadowAlpha * (1.0 - materialAlpha); vec3 rgb = color.rgb * materialAlpha + shadowColor.rgb * shadowAlpha * (1.0 - materialAlpha); fragColor = vec4(rgb, totalAlpha) * qt_Opacity; }