/** * 알파채널 경계 기반 BoxShadow 셰이더 * * image/sprite 객체에서 style.boxShadow* 를 * 이미지 사각형이 아닌 알파채널 외곽에 그림자를 렌더링합니다. * * 동작 방식: * - 현재 픽셀이 이미지 불투명 영역 위 → discard (그림자는 이미지 뒤에 숨겨짐) * - 그림자 소스 위치 = CSS offset 역변환: shadowSrcP = p - vec2(offsetX, -offsetY) * - blur: Gaussian 링 샘플링 (1 center + 3 rings × 8 = 25 samples) 으로 소프트 블러 근사 * - spread: effectiveBlur = blur + spread 로 그림자 확장 처리 */ export declare const alphaShadowVertex = "\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 alphaShadowFragment = "\n precision highp float;\n\n uniform sampler2D uTexture;\n uniform vec4 uColor; // \uADF8\uB9BC\uC790 \uC0C9\uC0C1 RGBA\n uniform float uOpacity; // \uC804\uCCB4 \uBD88\uD22C\uBA85\uB3C4\n uniform vec2 uQuadSize; // \uADF8\uB9BC\uC790 \uCFFC\uB4DC \uD06C\uAE30 (world units)\n uniform vec2 uImageSize; // \uC774\uBBF8\uC9C0 \uD45C\uC2DC \uD06C\uAE30 (world units)\n uniform vec2 uOffset; // CSS \uADF8\uB9BC\uC790 offset [x, y] (+y = down)\n uniform float uBlur; // \uBE14\uB7EC \uBC18\uACBD (world units)\n uniform float uSpread; // \uD655\uC7A5 \uBC18\uACBD (world units)\n uniform float uAlphaThreshold; // \uD53D\uC140\uC744 \uBD88\uD22C\uBA85\uC73C\uB85C \uD310\uB2E8\uD560 \uCD5C\uC19F\uAC12\n\n varying vec2 vUV;\n\n uniform vec2 uUVOffset;\n uniform vec2 uUVScale;\n\n // \uC774\uBBF8\uC9C0 UV \uBC94\uC704 \uD074\uB7A8\uD504 \uCCB4\uD06C\n bool inUV(vec2 uv) {\n return uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0;\n }\n\n // \uC6D4\uB4DC \uC88C\uD45C \u2192 \uC774\uBBF8\uC9C0 UV \uBCC0\uD658\n vec2 worldToImageUV(vec2 worldPos) {\n return (worldPos + uImageSize * 0.5) / uImageSize;\n }\n\n void main() {\n // \uADF8\uB9BC\uC790 \uCFFC\uB4DC \uC911\uC2EC \uAE30\uC900\uC758 \uD604\uC7AC \uD53D\uC140 \uC6D4\uB4DC \uC88C\uD45C\n vec2 p = (vUV - 0.5) * uQuadSize;\n\n // \uC774\uBBF8\uC9C0 \uBD88\uD22C\uBA85 \uC601\uC5ED \uC704\uB294 \uADF8\uB9BC\uC790 \uB80C\uB354 \uC81C\uC678\n vec2 imgUV = worldToImageUV(p);\n if (inUV(imgUV)) {\n vec2 texUV = imgUV * uUVScale + uUVOffset;\n if (texture2D(uTexture, texUV).a > uAlphaThreshold) {\n discard;\n }\n }\n\n // CSS \uADF8\uB9BC\uC790 offset \uC5ED\uBCC0\uD658: WebGL +y = up, CSS +y = down\n vec2 shadowSrcP = p - vec2(uOffset.x, -uOffset.y);\n\n // blur + spread \uB97C \uD569\uC0B0\uD558\uC5EC \uC720\uD6A8 \uBE14\uB7EC \uBC18\uACBD \uACC4\uC0B0\n float effectiveBlur = uBlur + uSpread;\n\n // \u2500\u2500 Gaussian \uB9C1 \uC0D8\uD50C\uB9C1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // 1 center + 3 rings \u00D7 8 samples = 25 samples\n float PI2 = 6.28318;\n float accAlpha = 0.0;\n float totalWeight = 0.0;\n\n // Ring 0: \uC911\uC2EC \uC0D8\uD50C (\uAC00\uC911\uCE58 \uCD5C\uB300)\n {\n float w = 1.0;\n vec2 sUV = worldToImageUV(shadowSrcP);\n if (inUV(sUV)) {\n vec2 texUV = sUV * uUVScale + uUVOffset;\n accAlpha += texture2D(uTexture, texUV).a * w;\n }\n totalWeight += w;\n }\n\n // Rings 1\u20133: \uC6D0\uC8FC \uBC29\uD5A5 8 \uC0D8\uD50C\uC529, Gaussian \uAC00\uC911\uCE58\n for (int ring = 1; ring <= 3; ring++) {\n float r = float(ring) * effectiveBlur / 3.0;\n float sigma = max(effectiveBlur / 2.0, 0.001);\n float w = exp(-r * r / (2.0 * sigma * sigma));\n\n for (int i = 0; i < 8; i++) {\n float angle = float(i) / 8.0 * PI2;\n vec2 s = shadowSrcP + vec2(cos(angle), sin(angle)) * r;\n vec2 sUV = worldToImageUV(s);\n if (inUV(sUV)) {\n vec2 texUV = sUV * uUVScale + uUVOffset;\n accAlpha += texture2D(uTexture, texUV).a * w;\n }\n totalWeight += w;\n }\n }\n\n float shadowAlpha = accAlpha / max(totalWeight, 0.001);\n if (shadowAlpha <= uAlphaThreshold) discard;\n\n float finalA = uColor.a * uOpacity * shadowAlpha;\n gl_FragColor = vec4(uColor.rgb * finalA, finalA);\n }\n";