responsive-image-canvas/dist/distortion.frag.glsl
BaekRyang e08f34caab feat: Add per-area physics configuration
- 영역별 물리 설정 `physics` 옵션을 추가했습니다.
- `useMouseInteraction` 훅에서 영역별 물리 설정을 적용하도록 수정했습니다.
- `ImageDistortion` 컴포넌트에서 `selectedAreaId` prop을 제거하고, 마우스 인터랙션 시 `interactingAreaIndices`를 활용하도록 변경했습니다.
- 셰이더에서 텍스처 좌표가 범위를 벗어날 때 부드럽게 페이드 아웃되도록 수정했습니다.
2025-11-24 14:23:06 +09:00

94 lines
3.0 KiB
GLSL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

uniform vec2 u_resolution;
uniform sampler2D u_texture;
uniform vec2 u_points[32]; // 최대 8영역 × 4포인트 (정규화된 좌표 0-1)
uniform int u_numAreas;
uniform vec2 u_dragVectors[8]; // 드래그 벡터 (정규화된 좌표 0-1)
uniform float u_distortionStrengths[8];
varying vec2 vUv;
// Flutter 원본 computeUV 함수 (정확히 동일하게 변환)
vec2 computeUV(vec2 xy, vec2 p0, vec2 p1, vec2 p2, vec2 p3) {
vec2 minP = min(min(p0, p1), min(p2, p3));
vec2 maxP = max(max(p0, p1), max(p2, p3));
if (xy.x < minP.x || xy.x > maxP.x || xy.y < minP.y || xy.y > maxP.y) {
return vec2(-1.0, -1.0); // 외부
}
vec2 rectSize = maxP - minP;
if (rectSize.x == 0.0 || rectSize.y == 0.0) {
return vec2(-1.0, -1.0); // 축퇴
}
vec2 rectMin = minP;
vec2 rectUV = (xy - rectMin) / rectSize;
float u0 = rectUV.x;
float v0 = rectUV.y;
// 1회 Newton-Raphson (Flutter 원본과 동일)
vec2 left = mix(p0, p1, u0);
vec2 right = mix(p3, p2, u0);
vec2 xy0 = mix(left, right, v0);
vec2 dxy = xy - xy0;
vec2 du_vec = mix(p1 - p0, p2 - p3, v0);
vec2 dv_vec = mix(p3 - p0, p2 - p1, u0);
float det = du_vec.x * dv_vec.y - du_vec.y * dv_vec.x;
if (abs(det) > 1e-6) {
float inv_det = 1.0 / det;
float du = (dv_vec.y * dxy.x - dv_vec.x * dxy.y) * inv_det;
float dv = (-du_vec.y * dxy.x + du_vec.x * dxy.y) * inv_det;
u0 += du;
v0 += dv;
}
return vec2(u0, v0);
}
void main() {
vec2 xy = vUv * u_resolution; // 픽셀 좌표
vec2 texCoord = vUv;
// 모든 겹치는 영역의 왜곡을 누적 적용
for (int i = 0; i < 8; i++) {
if (i >= u_numAreas) break;
// 포인트는 정규화된 좌표로 전달받았으므로 픽셀 좌표로 변환
vec2 p0 = u_points[i * 4 + 0] * u_resolution;
vec2 p1 = u_points[i * 4 + 1] * u_resolution;
vec2 p2 = u_points[i * 4 + 2] * u_resolution;
vec2 p3 = u_points[i * 4 + 3] * u_resolution;
vec2 uv_local = computeUV(xy, p0, p1, p2, p3);
if (uv_local.x >= 0.0 && uv_local.x <= 1.0 && uv_local.y >= 0.0 && uv_local.y <= 1.0) {
vec2 uvCenter = vec2(0.5, 0.5);
float distToCenter = distance(uv_local, uvCenter);
float maxUvRadius = 0.5;
if (distToCenter < maxUvRadius) {
float influence = 1.0 - smoothstep(0.0, maxUvRadius, distToCenter);
// dragVector는 정규화된 좌표(0-1)이므로 바로 사용
vec2 distortion = u_dragVectors[i] * influence * u_distortionStrengths[i];
texCoord += distortion;
}
}
}
// 경계 근처에서 부드럽게 페이드 아웃
// 텍스처 좌표가 0~1 범위를 벗어나면 알파값을 줄여서 자연스럽게 처리
vec2 edgeDist = min(texCoord, 1.0 - texCoord);
float edgeFade = smoothstep(0.0, 0.05, min(edgeDist.x, edgeDist.y));
// 범위를 벗어난 좌표는 fract로 래핑하여 반복 효과 (더 자연스러움)
vec2 wrappedCoord = fract(texCoord);
vec4 color = texture2D(u_texture, wrappedCoord);
// 경계에서 페이드 아웃 적용
color.a *= edgeFade;
gl_FragColor = color;
}