BaekRyang e371321fd2 feat: Fix image distortion shader and improve loading state
- Fix distortion.frag.glsl to match Flutter original implementation
  - Update computeUV function with single Newton-Raphson iteration
  - Fix coordinate transformation (normalized to pixel)
  - Fix distortion application logic
  - Add break after first matching area (Flutter behavior)

- Add image loading state management
  - Add imageLoaded state
  - Add loading progress callback
  - Add loading UI indicator
  - Improve error handling

- Add comprehensive debug logging
  - ShaderManager: fetch status and shader lengths
  - ThreeScene: shader compilation check, render calls
  - ImageDistortion: lifecycle and loading status

- Add test/debug shaders for troubleshooting
  - test.frag.glsl: Simple pass-through shader
  - debug.frag.glsl: Area visualization shader

- Fix infinite loop bug in animationCallback
  - Use setState updater function to avoid dependency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 11:51:39 +09:00

1 line
27 KiB
Plaintext
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.

{"version":3,"sources":["../src/components/ImageDistortion.tsx","../src/engine/ThreeScene.ts","../src/engine/ShaderManager.ts","../src/utils/easing.ts","../src/engine/AnimationLoop.ts","../src/hooks/useAnimationFrame.ts","../src/utils/constants.ts"],"sourcesContent":["import React, { useEffect, useRef, useState, useCallback } from 'react';\nimport * as THREE from 'three';\nimport { type DistortionArea } from '../types';\nimport { ThreeScene } from '../engine/ThreeScene';\nimport { ShaderManager } from '../engine/ShaderManager';\nimport { AnimationLoop } from '../engine/AnimationLoop';\nimport { useAnimationFrame } from '../hooks/useAnimationFrame';\nimport { SHADER_CONFIG } from '../utils/constants';\n\n/**\n * ImageDistortion 컴포넌트 Props\n */\nexport interface ImageDistortionProps {\n /** 이미지 소스 URL */\n imageSrc: string;\n /** 왜곡 영역 배열 */\n areas: DistortionArea[];\n /** 버텍스 셰이더 경로 (선택사항) */\n vertexShaderPath?: string;\n /** 프래그먼트 셰이더 경로 (선택사항) */\n fragmentShaderPath?: string;\n /** 애니메이션 재생 여부 */\n isPlaying?: boolean;\n /** 컨테이너 스타일 */\n style?: React.CSSProperties;\n /** 컨테이너 클래스명 */\n className?: string;\n}\n\n/**\n * GPU 가속 이미지 왜곡 컴포넌트\n * Three.js와 GLSL 셰이더를 사용하여 실시간 이미지 왜곡 효과를 제공합니다.\n */\nexport const ImageDistortion: React.FC<ImageDistortionProps> = ({\n imageSrc,\n areas,\n vertexShaderPath,\n fragmentShaderPath,\n isPlaying = true,\n style,\n className,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const sceneRef = useRef<ThreeScene | null>(null);\n const shaderManagerRef = useRef<ShaderManager>(new ShaderManager());\n const textureRef = useRef<THREE.Texture | null>(null);\n\n const [isReady, setIsReady] = useState(false);\n const [imageLoaded, setImageLoaded] = useState(false);\n const [currentAreas, setCurrentAreas] = useState<DistortionArea[]>(areas);\n\n // 영역 변경 시 상태 업데이트\n useEffect(() => {\n setCurrentAreas(areas);\n }, [areas]);\n\n // Three.js 씬 초기화\n useEffect(() => {\n console.log('[ImageDistortion] useEffect 실행, containerRef.current:', containerRef.current);\n\n if (!containerRef.current) {\n console.warn('[ImageDistortion] containerRef.current가 null입니다. 컴포넌트가 제대로 마운트되지 않았습니다.');\n return;\n }\n\n console.log('[ImageDistortion] 초기화 시작');\n const scene = new ThreeScene(containerRef.current);\n sceneRef.current = scene;\n\n // 셰이더 로드\n const vertPath = vertexShaderPath || '/shaders/distortion.vert.glsl';\n const fragPath = fragmentShaderPath || '/shaders/distortion.frag.glsl';\n\n console.log('[ImageDistortion] 셰이더 로드 시도:', { vertPath, fragPath });\n\n shaderManagerRef.current\n .loadShaders(vertPath, fragPath)\n .then(({ vertex, fragment }) => {\n console.log('[ImageDistortion] 셰이더 로드 성공');\n scene.setShaderMaterial(vertex, fragment);\n setIsReady(true);\n })\n .catch((error) => {\n console.error('[ImageDistortion] 셰이더 로드 실패:', error);\n });\n\n return () => {\n scene.dispose();\n if (textureRef.current) {\n textureRef.current.dispose();\n }\n };\n }, [vertexShaderPath, fragmentShaderPath]);\n\n // 이미지 텍스처 로드\n useEffect(() => {\n if (!imageSrc || !isReady) {\n console.log('[ImageDistortion] 이미지 로드 스킵:', { imageSrc, isReady });\n return;\n }\n\n console.log('[ImageDistortion] 이미지 로드 시작:', imageSrc);\n setImageLoaded(false);\n\n const loader = new THREE.TextureLoader();\n loader.load(\n imageSrc,\n (texture) => {\n console.log('[ImageDistortion] 이미지 로드 성공!', {\n width: texture.image.width,\n height: texture.image.height\n });\n textureRef.current = texture;\n setImageLoaded(true);\n if (sceneRef.current) {\n sceneRef.current.updateUniforms({\n u_texture: { value: texture },\n });\n sceneRef.current.render();\n console.log('[ImageDistortion] 텍스처 업데이트 및 렌더링 완료');\n }\n },\n (progress) => {\n console.log('[ImageDistortion] 이미지 로딩 중...',\n Math.round((progress.loaded / progress.total) * 100) + '%'\n );\n },\n (error) => {\n console.error('[ImageDistortion] 이미지 로드 실패:', error);\n setImageLoaded(false);\n }\n );\n\n return () => {\n if (textureRef.current) {\n textureRef.current.dispose();\n textureRef.current = null;\n }\n };\n }, [imageSrc, isReady]);\n\n // 셰이더 유니폼 업데이트\n useEffect(() => {\n if (!sceneRef.current || !isReady) return;\n\n // 포인트 배열 생성\n const points = new Float32Array(SHADER_CONFIG.MAX_POINTS * 2);\n currentAreas.forEach((area, areaIndex) => {\n area.basePoints.forEach((point, pointIndex) => {\n const index = (areaIndex * 4 + pointIndex) * 2;\n points[index] = point.x;\n points[index + 1] = point.y;\n });\n });\n\n // 드래그 벡터 배열 생성\n const dragVectors = new Float32Array(SHADER_CONFIG.MAX_DRAG_VECTORS * 2);\n currentAreas.forEach((area, index) => {\n const baseIndex = index * 2;\n dragVectors[baseIndex] = area.dragVector.x;\n dragVectors[baseIndex + 1] = area.dragVector.y;\n });\n\n // 강도 배열 생성\n const strengths = new Float32Array(SHADER_CONFIG.MAX_STRENGTHS);\n currentAreas.forEach((area, index) => {\n strengths[index] = area.distortionStrength;\n });\n\n sceneRef.current.updateUniforms({\n u_numAreas: { value: currentAreas.length },\n u_points: { value: points },\n u_dragVectors: { value: dragVectors },\n u_distortionStrengths: { value: strengths },\n });\n\n sceneRef.current.render();\n }, [currentAreas, isReady]);\n\n // 애니메이션 루프\n const animationCallback = useCallback((deltaTime: number) => {\n if (!isReady) return;\n\n setCurrentAreas((prevAreas) => {\n // 진행도 업데이트\n const updatedAreas = AnimationLoop.updateProgress(prevAreas, deltaTime);\n // 드래그 벡터 업데이트\n return AnimationLoop.updateAreaDragVectors(updatedAreas);\n });\n }, [isReady]);\n\n useAnimationFrame(animationCallback, isPlaying);\n\n return (\n <div\n ref={containerRef}\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n ...style,\n }}\n className={className}\n >\n {!imageLoaded && (\n <div\n style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n background: 'rgba(0, 0, 0, 0.7)',\n color: 'white',\n padding: '20px',\n borderRadius: '8px',\n zIndex: 999,\n }}\n >\n 이미지 로딩 중...\n </div>\n )}\n </div>\n );\n};","import * as THREE from 'three';\nimport type { ShaderUniforms } from '../types';\n\n/**\n * Three.js 씬 관리 클래스\n */\nexport class ThreeScene {\n private scene: THREE.Scene;\n private camera: THREE.OrthographicCamera;\n private renderer: THREE.WebGLRenderer;\n private mesh: THREE.Mesh | null = null;\n private uniforms: ShaderUniforms;\n\n constructor(private container: HTMLElement) {\n // 씬 생성\n this.scene = new THREE.Scene();\n\n // 2D용 직교 카메라 설정\n this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n // 렌더러 설정\n this.renderer = new THREE.WebGLRenderer({\n antialias: true,\n alpha: false,\n });\n this.renderer.setPixelRatio(window.devicePixelRatio);\n this.container.appendChild(this.renderer.domElement);\n\n // 유니폼 초기화\n this.uniforms = {\n u_resolution: { value: new THREE.Vector2() },\n u_texture: { value: null },\n u_points: { value: new Float32Array(64) }, // 32포인트 × 2(x,y)\n u_numAreas: { value: 0 },\n u_dragVectors: { value: new Float32Array(16) }, // 8벡터 × 2(x,y)\n u_distortionStrengths: { value: new Float32Array(8) },\n };\n\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n }\n\n /**\n * 윈도우 리사이즈 핸들러\n */\n private handleResize = () => {\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n\n this.renderer.setSize(width, height);\n this.uniforms.u_resolution.value.set(width, height);\n\n if (this.mesh) {\n this.render();\n }\n };\n\n /**\n * 셰이더 머티리얼 설정\n * @param vertexShader 버텍스 셰이더 소스\n * @param fragmentShader 프래그먼트 셰이더 소스\n */\n public setShaderMaterial(vertexShader: string, fragmentShader: string) {\n console.log('[ThreeScene] setShaderMaterial 호출됨');\n console.log('[ThreeScene] vertexShader 길이:', vertexShader.length);\n console.log('[ThreeScene] fragmentShader 길이:', fragmentShader.length);\n\n const geometry = new THREE.PlaneGeometry(2, 2);\n const material = new THREE.ShaderMaterial({\n uniforms: this.uniforms,\n vertexShader,\n fragmentShader,\n });\n\n console.log('[ThreeScene] ShaderMaterial 생성됨');\n\n // 셰이더 컴파일 에러 확인\n const renderer = this.renderer;\n const testScene = new THREE.Scene();\n const testMesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material);\n testScene.add(testMesh);\n\n try {\n renderer.compile(testScene, this.camera);\n console.log('[ThreeScene] 셰이더 컴파일 성공!');\n } catch (e) {\n console.error('[ThreeScene] 셰이더 컴파일 에러:', e);\n }\n\n if (this.mesh) {\n this.scene.remove(this.mesh);\n }\n\n this.mesh = new THREE.Mesh(geometry, material);\n this.scene.add(this.mesh);\n console.log('[ThreeScene] mesh를 씬에 추가함');\n }\n\n /**\n * 유니폼 값 업데이트\n * @param updates 업데이트할 유니폼 값들\n */\n public updateUniforms(updates: Partial<ShaderUniforms>) {\n Object.keys(updates).forEach((key) => {\n const uniformKey = key as keyof ShaderUniforms;\n this.uniforms[uniformKey].value = updates[uniformKey]!.value;\n });\n }\n\n /**\n * 씬 렌더링\n */\n public render() {\n console.log('[ThreeScene] render() 호출됨, mesh:', this.mesh);\n this.renderer.render(this.scene, this.camera);\n }\n\n /**\n * 리소스 정리\n */\n public dispose() {\n window.removeEventListener('resize', this.handleResize);\n this.renderer.dispose();\n if (this.mesh) {\n this.mesh.geometry.dispose();\n (this.mesh.material as THREE.Material).dispose();\n }\n if (this.container.contains(this.renderer.domElement)) {\n this.container.removeChild(this.renderer.domElement);\n }\n }\n}","/**\n * 셰이더 파일 로딩 및 관리 클래스\n */\nexport class ShaderManager {\n private vertexShaderSource: string | null = null;\n private fragmentShaderSource: string | null = null;\n\n /**\n * 셰이더 파일들을 비동기로 로드\n * @param vertexPath 버텍스 셰이더 파일 경로\n * @param fragmentPath 프래그먼트 셰이더 파일 경로\n * @returns 로드된 셰이더 소스 코드\n */\n public async loadShaders(\n vertexPath: string,\n fragmentPath: string\n ): Promise<{ vertex: string; fragment: string }> {\n console.log('[ShaderManager] loadShaders 시작:', { vertexPath, fragmentPath });\n\n try {\n console.log('[ShaderManager] fetch 시작...');\n const [vertexResponse, fragmentResponse] = await Promise.all([\n fetch(vertexPath),\n fetch(fragmentPath),\n ]);\n\n console.log('[ShaderManager] fetch 완료:', {\n vertexStatus: vertexResponse.status,\n fragmentStatus: fragmentResponse.status\n });\n\n if (!vertexResponse.ok) {\n throw new Error(`버텍스 셰이더 로드 실패: ${vertexResponse.statusText}`);\n }\n if (!fragmentResponse.ok) {\n throw new Error(`프래그먼트 셰이더 로드 실패: ${fragmentResponse.statusText}`);\n }\n\n console.log('[ShaderManager] text() 변환 시작...');\n this.vertexShaderSource = await vertexResponse.text();\n this.fragmentShaderSource = await fragmentResponse.text();\n\n console.log('[ShaderManager] 셰이더 로드 완료!', {\n vertexLength: this.vertexShaderSource.length,\n fragmentLength: this.fragmentShaderSource.length\n });\n\n return {\n vertex: this.vertexShaderSource,\n fragment: this.fragmentShaderSource,\n };\n } catch (error) {\n console.error('[ShaderManager] 셰이더 로드 실패:', error);\n throw new Error('셰이더 로딩에 실패했습니다');\n }\n }\n\n /**\n * 버텍스 셰이더 소스 코드 반환\n */\n public getVertexShader(): string {\n if (!this.vertexShaderSource) {\n throw new Error('버텍스 셰이더가 로드되지 않았습니다');\n }\n return this.vertexShaderSource;\n }\n\n /**\n * 프래그먼트 셰이더 소스 코드 반환\n */\n public getFragmentShader(): string {\n if (!this.fragmentShaderSource) {\n throw new Error('프래그먼트 셰이더가 로드되지 않았습니다');\n }\n return this.fragmentShaderSource;\n }\n}","import { type EasingFunction } from '../types';\n\ntype EasingFunc = (t: number) => number;\n\n/**\n * 이징 함수 구현 맵\n */\nconst easingFunctions: Record<EasingFunction, EasingFunc> = {\n linear: (t) => t,\n\n easeIn: (t) => t * t,\n easeOut: (t) => t * (2 - t),\n easeInOut: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),\n\n easeInQuad: (t) => t * t,\n easeOutQuad: (t) => t * (2 - t),\n};\n\n/**\n * 진행도에 이징 함수를 적용\n * @param progress 진행도 (0.0 - 1.0)\n * @param easingType 적용할 이징 함수 타입\n * @returns 이징이 적용된 진행도 (0.0 - 1.0)\n */\nexport const applyEasing = (\n progress: number,\n easingType: EasingFunction\n): number => {\n const clampedProgress = Math.max(0, Math.min(1, progress));\n return easingFunctions[easingType](clampedProgress);\n};","import { applyEasing } from '../utils/easing';\nimport type {DistortionArea, Point} from \"../types\";\n\n/**\n * 애니메이션 루프 관리 클래스\n */\nexport class AnimationLoop {\n /**\n * 영역들의 드래그 벡터를 현재 진행도에 따라 업데이트\n * @param areas 왜곡 영역 배열\n * @returns 업데이트된 영역 배열\n */\n public static updateAreaDragVectors(\n areas: DistortionArea[]\n ): DistortionArea[] {\n return areas.map((area) => {\n const { progress, movement } = area;\n\n // 이징 적용\n const easedProgress = applyEasing(progress, movement.easing);\n\n // 벡터 간 보간\n let dragVector: Point;\n\n if (easedProgress < 0.5) {\n // 0.0 -> 0.5: 0에서 vectorA로 보간\n const t = easedProgress * 2;\n dragVector = {\n x: movement.vectorA.x * t,\n y: movement.vectorA.y * t,\n };\n } else {\n // 0.5 -> 1.0: vectorA에서 0으로 보간\n const t = (easedProgress - 0.5) * 2;\n dragVector = {\n x: movement.vectorA.x * (1 - t),\n y: movement.vectorA.y * (1 - t),\n };\n }\n\n return {\n ...area,\n dragVector,\n };\n });\n }\n\n /**\n * 모든 영역의 진행도를 델타 타임만큼 업데이트\n * @param areas 왜곡 영역 배열\n * @param deltaTime 델타 타임 (초)\n * @returns 업데이트된 영역 배열\n */\n public static updateProgress(\n areas: DistortionArea[],\n deltaTime: number\n ): DistortionArea[] {\n return areas.map((area) => {\n let newProgress = area.progress + deltaTime / area.movement.duration;\n newProgress %= 1.0; // 루프\n\n return {\n ...area,\n progress: newProgress,\n };\n });\n }\n}","import { useEffect, useRef } from 'react';\n\n/**\n * requestAnimationFrame을 사용한 애니메이션 루프 훅\n * @param callback 매 프레임마다 호출될 콜백 (deltaTime을 인자로 받음)\n * @param isPlaying 애니메이션 재생 여부\n */\nexport const useAnimationFrame = (\n callback: (deltaTime: number) => void,\n isPlaying: boolean = true\n) => {\n const requestRef = useRef<number | undefined>(undefined);\n const previousTimeRef = useRef<number | undefined>(undefined);\n\n useEffect(() => {\n if (!isPlaying) return;\n\n const animate = (time: number) => {\n if (previousTimeRef.current !== undefined) {\n const deltaTime = (time - previousTimeRef.current) / 1000; // 밀리초를 초로 변환\n callback(deltaTime);\n }\n previousTimeRef.current = time;\n requestRef.current = requestAnimationFrame(animate);\n };\n\n requestRef.current = requestAnimationFrame(animate);\n\n return () => {\n if (requestRef.current) {\n cancelAnimationFrame(requestRef.current);\n }\n };\n }, [callback, isPlaying]);\n};","/**\n * 셰이더 관련 설정\n */\nexport const SHADER_CONFIG = {\n /** 최대 영역 개수 */\n MAX_AREAS: 8,\n /** 최대 포인트 개수 (8영역 × 4포인트) */\n MAX_POINTS: 32,\n /** 최대 드래그 벡터 개수 */\n MAX_DRAG_VECTORS: 8,\n /** 최대 강도 배열 크기 */\n MAX_STRENGTHS: 8,\n} as const;\n\n/**\n * 애니메이션 관련 설정\n */\nexport const ANIMATION_CONFIG = {\n /** 목표 FPS */\n TARGET_FPS: 60,\n /** 델타 타임 (약 16.67ms) */\n DELTA_TIME: 1 / 60,\n} as const;\n\n/**\n * 기본 영역 설정값\n */\nexport const DEFAULT_AREA = {\n /** 기본 왜곡 강도 */\n DISTORTION_STRENGTH: 0.5,\n /** 기본 애니메이션 지속 시간 (초) */\n DURATION: 2.0,\n /** 기본 이징 함수 */\n EASING: 'easeInOut' as const,\n /** 기본 벡터 A */\n VECTOR_A: { x: 0.1, y: 0.1 },\n /** 기본 벡터 B */\n VECTOR_B: { x: -0.1, y: -0.1 },\n} as const;"],"mappings":"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","names":["useEffect","useRef","THREE","useRef","useEffect"]}