/** * 알파채널 경계 기반 Border + Outline 셰이더 * * image/sprite 객체에서 style.borderColor, style.outlineColor 를 * 이미지 사각형이 아닌 알파채널 외곽에 렌더링합니다. * * 동작 방식: * - 확장 쿼드(expanded quad) 전체에서 실행됩니다. * expandedW = drawW + 2 * (borderWidth + outlineWidth) * - 불투명 픽셀(이미지 내부): discard → 별도 texture 패스에서 렌더됨 * - border 범위 내 픽셀: borderColor 렌더 * - outline 범위 내 픽셀: outlineColor 렌더 * - 범위 초과: discard * * 제한: * - MAX_RADIUS = 16 → borderWidth + outlineWidth 합계 최대 16 world units * 더 큰 값이 필요하면 MAX_RADIUS 상수를 수정하십시오 (성능 비례 증가) */ export declare const alphaOutlineVertex = "\n attribute vec2 position;\n attribute vec2 uv;\n uniform mat4 uModelMatrix;\n uniform mat4 uViewMatrix;\n uniform mat4 uProjectionMatrix;\n varying vec2 vUV;\n\n void main() {\n vUV = uv;\n gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(position, 0.0, 1.0);\n }\n"; export declare const alphaOutlineFragment = "\n precision highp float;\n\n uniform sampler2D uTexture;\n uniform float uOpacity;\n uniform float uAlphaThreshold;\n\n uniform vec2 uUVOffset;\n uniform vec2 uUVScale;\n\n // \uD655\uC7A5 \uCFFC\uB4DC \u2192 \uC774\uBBF8\uC9C0 UV \uBCC0\uD658 \uD30C\uB77C\uBBF8\uD130\n uniform vec2 uImageOffset; // \uD655\uC7A5 \uCFFC\uB4DC UV\uC5D0\uC11C \uC774\uBBF8\uC9C0 \uC2DC\uC791\uC810 (pad/expandedW)\n uniform vec2 uImageScale; // \uD655\uC7A5 \uCFFC\uB4DC UV\uC5D0\uC11C \uC774\uBBF8\uC9C0\uAC00 \uCC28\uC9C0\uD558\uB294 \uBE44\uC728 (drawW/expandedW)\n\n // \uC774\uBBF8\uC9C0 UV \uACF5\uAC04\uC5D0\uC11C 1 world pixel \uD06C\uAE30 = vec2(1/drawW, 1/drawH)\n uniform vec2 uTexelStep;\n\n uniform float uBorderWidth; // world pixels\n uniform vec4 uBorderColor; // premultiplied-ready RGBA\n uniform float uOutlineWidth; // world pixels\n uniform vec4 uOutlineColor; // premultiplied-ready RGBA\n\n varying vec2 vUV;\n\n #define MAX_RADIUS 16\n\n void main() {\n // 1. \uD655\uC7A5 \uCFFC\uB4DC UV \u2192 \uC774\uBBF8\uC9C0 UV \uBCC0\uD658\n vec2 imageUV = (vUV - uImageOffset) / uImageScale;\n bool inImage = imageUV.x >= 0.0 && imageUV.x <= 1.0\n && imageUV.y >= 0.0 && imageUV.y <= 1.0;\n\n // 2. \uC774\uBBF8\uC9C0 \uB0B4 \uBD88\uD22C\uBA85 \uD53D\uC140\uC740 main texture \uD328\uC2A4\uC5D0\uC11C \uB80C\uB354\uB428 \u2192 \uC5EC\uAE30\uC120 \uC81C\uC678\n if (inImage) {\n vec2 texUV = imageUV * uUVScale + uUVOffset;\n if (texture2D(uTexture, texUV).a > uAlphaThreshold) {\n discard;\n }\n }\n\n // 3. \uC778\uC811 \uD53D\uC140 \uD0D0\uC0C9: \uBD88\uD22C\uBA85 \uD53D\uC140\uAE4C\uC9C0\uC758 \uCD5C\uC18C \uAC70\uB9AC(world pixels)\n float searchR = uBorderWidth + uOutlineWidth;\n float minDist = 99999.0;\n\n for (int dx = -MAX_RADIUS; dx <= MAX_RADIUS; dx++) {\n for (int dy = -MAX_RADIUS; dy <= MAX_RADIUS; dy++) {\n float d = length(vec2(float(dx), float(dy)));\n if (d > searchR) continue;\n vec2 sUV = imageUV + vec2(float(dx), float(dy)) * uTexelStep;\n if (sUV.x < 0.0 || sUV.x > 1.0 || sUV.y < 0.0 || sUV.y > 1.0) continue;\n \n vec2 sTexUV = sUV * uUVScale + uUVOffset;\n if (texture2D(uTexture, sTexUV).a > uAlphaThreshold) {\n if (d < minDist) minDist = d;\n }\n }\n }\n\n if (minDist > searchR) {\n discard;\n }\n\n // 4. \uAC70\uB9AC \uAD6C\uAC04\uBCC4 \uC0C9\uC0C1 \uACB0\uC815 (border \u2192 outline \uC21C\uC11C)\n if (uBorderWidth > 0.0 && minDist <= uBorderWidth) {\n float a = uBorderColor.a * uOpacity;\n gl_FragColor = vec4(uBorderColor.rgb * a, a);\n } else if (uOutlineWidth > 0.0 && minDist <= searchR) {\n float a = uOutlineColor.a * uOpacity;\n gl_FragColor = vec4(uOutlineColor.rgb * a, a);\n } else {\n discard;\n }\n }\n";