- 스프라이트 기반 파티클 이펙트 관리 기능 추가 (SpriteEffectManager) - 렌즈 왜곡 셰이더 로직 개선 및 픽셀 공간 기준 등방성 확대 적용 - 파티클 최적화를 위한 오브젝트 풀링(SpriteParticlePool) 도입 - DistortionArea 타입에 spriteEffects 설정 필드 추가 - ThreeScene에 씬 객체 접근 기능 및 렌더 순서 제어 추가 - useMouseInteraction 훅에서 마우스 상태 조회 기능 추가 - 버전 1.3.0 업데이트 및 관련 타입 정의 반영
119 lines
4.3 KiB
GLSL
119 lines
4.3 KiB
GLSL
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];
|
||
uniform float u_lensEffects[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;
|
||
|
||
// 렌즈 왜곡 효과 (볼록: 중심 확대, 오목: 중심 축소)
|
||
if (abs(u_lensEffects[i]) > 0.001) {
|
||
// 영역 중심의 글로벌 UV 좌표
|
||
vec2 minP_area = min(min(p0, p1), min(p2, p3));
|
||
vec2 maxP_area = max(max(p0, p1), max(p2, p3));
|
||
vec2 areaSize = maxP_area - minP_area;
|
||
vec2 areaCenterUV = (minP_area + maxP_area) * 0.5 / u_resolution;
|
||
|
||
// 현재 픽셀에서 영역 중심까지의 글로벌 UV 오프셋
|
||
vec2 offset = vUv - areaCenterUV;
|
||
// 픽셀 공간 거리로 원형 감쇠 (긴 변 기준으로 영역 전체 커버)
|
||
float distPx = length(offset * u_resolution);
|
||
float maxRadiusPx = max(areaSize.x, areaSize.y) * 0.5;
|
||
float normalizedDist = distPx / maxRadiusPx;
|
||
|
||
if (normalizedDist < 1.0) {
|
||
// 중심에서 최대 강도, 가장자리로 갈수록 자연스럽게 0으로 감소
|
||
float lensAmount = u_lensEffects[i] * (1.0 - normalizedDist * normalizedDist);
|
||
// 볼록(+): 텍스처 좌표를 중심으로 당김 → 확대
|
||
// offset은 글로벌 UV이므로 픽셀 공간에서 등방성(isotropic) 확대
|
||
texCoord -= offset * lensAmount * u_distortionStrengths[i];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 경계 근처에서 부드럽게 페이드 아웃
|
||
// 텍스처 좌표가 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;
|
||
} |