Skip to content

jglee96/path-gradation-webgl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Path Gradation - WebGL SDF

MS Office PowerPoint의 경로 그라데이션(Path Gradation) 효과를 웹에서 구현한 프로젝트. 도형의 윤곽선으로부터 중심 방향으로 그라데이션이 적용되며, 도형의 모양을 따라 자연스럽게 색상이 전환된다.

실행

python3 -m http.server 8080
# http://localhost:8080 접속

빌드 시스템 없이 순수 ES modules 사용. 로컬 서버만 필요.

핵심 알고리즘: Signed Distance Field (SDF)

왜 SDF인가

기존 시도 문제점
SVG tessellation (삼각형별 linear gradient) 삼각형 경계에서 seam 발생
GLSL (픽셀이 속한 삼각형의 한 변까지 거리) 모든 변까지의 최소 거리를 구해야 정확

SDF 방식: 각 픽셀에서 폴리곤의 모든 edge까지의 최소 거리를 계산하여 seam 없는 gradient를 생성한다.

SDF 계산 (Inigo Quilez 알고리즘)

Fragment shader에서 매 픽셀마다 실행:

1. 폴리곤의 모든 edge를 순회
2. 각 edge에 대해 point-to-segment 최소 거리 계산
3. 전체 edge 중 최솟값 = 해당 픽셀의 윤곽선까지 거리
4. Winding number로 inside/outside 판정 (부호 결정)
5. 내부 거리를 0~1로 정규화 → gradient color stop에 매핑

외부 픽셀은 discard, 내부 픽셀은 abs(sdf) / maxDist로 정규화한다.

거리 정규화

gradient를 0~1 범위로 매핑하려면 도형 내부의 최대 거리(maxDist)가 필요하다. CPU에서 바운딩박스 내 그리드 샘플링으로 사전 계산한다:

  • 고품질 렌더링: 100x100 그리드
  • 드래그 중 실시간 렌더링: 40x40 그리드 (성능 최적화)

정점 데이터 파이프라인

SVG path string
    ↓  pathToPolygon() - 브라우저 SVG API (getPointAtLength)로 300개 점 샘플링
Float32Array [x0,y0, x1,y1, ...]
    ↓  fitToCanvas() - 캔버스 좌표계로 정규화 (600x600, margin 80)
baseVertices (기준 정점)
    ↓  transformVertices() - shapeState {cx, cy, width, height, rotation} 적용
transformed vertices
    ↓  RG32F 1D 텍스처로 GPU 업로드 (texelFetch로 읽기)
Fragment shader에서 SDF 계산

왜 float texture인가: uniform 배열은 크기 제한이 있지만, 텍스처는 가변 길이 정점을 제한 없이 전달할 수 있다.

도형 편집 (PPT-style Handles)

Canvas 위에 HTML/CSS 오버레이로 핸들을 렌더링한다:

  • 8개 리사이즈 핸들: 4 모서리 + 4 변 중점. 반대편 핸들을 앵커로 고정하고 드래그 방향으로 scale 적용.
  • 1개 회전 핸들: 상단 초록색 원. 도형 중심 기준 회전. Shift 키로 15도 스냅.
  • 커서 회전: 도형 회전각에 따라 핸들 커서 방향도 함께 회전 (PPT 동일).

리사이즈 수학

모서리 핸들 드래그 시:

anchor = 반대편 모서리의 월드 좌표 (고정점)
newCenter = (anchor + mouse) / 2
localOffset = rotate(mouse - newCenter, -rotation)
newWidth = |localOffset.x| * 2
newHeight = |localOffset.y| * 2

변 핸들 드래그 시 한 축만 변경:

mouse - anchor 벡터를 해당 축(로컬 X 또는 Y)에 투영
해당 축의 크기만 갱신, 중심도 축 방향으로만 이동

실시간 성능

드래그 중 매 프레임마다 전체 파이프라인(정점 변환 → maxDist 계산 → GPU 업로드 → SDF 렌더링)이 실행된다. renderFast()(gridRes=40)로 maxDist 계산 비용을 줄이고, mouseup 시 renderFull()(gridRes=100)로 고품질 재렌더링한다.

파일 구조

index.html      캔버스, UI 컨트롤, 핸들 CSS
main.js         WebGL2 셋업, 도형 상태 관리, 렌더링 루프
shaders.js      Vertex/Fragment shader (SDF 계산 핵심)
sdf.js          CPU측 SDF 계산 (maxDist 정규화용)
path-utils.js   SVG path → polygon 변환, 프리셋 도형 5종
handles.js      PPT-style 리사이즈/회전 핸들 시스템

Future Work: Electron 앱 통합

현재 프로젝트는 독립 데모로, SDF 알고리즘과 shader 코드의 정확성을 검증하는 것이 목적이다. 이를 Electron 기반 편집기에 새로운 gradation 태그로 통합하려면 아래 작업이 필요하다.

1. 문서 모델 (Document Schema)

현재는 shapeState가 JS 변수로만 존재하지만, 실제 앱에서는 문서 포맷에 정의되어야 한다.

  • OOXML의 <a:pathGradFill> 파싱 또는 자체 포맷에 새 태그 정의
  • gradient stops, 도형 path, 방향 등의 속성 직렬화/역직렬화
  • 문서 저장/로드 시 path gradation 상태 완전 복원

2. 렌더링 파이프라인 통합

현재는 canvas 하나에 도형 하나를 전체 렌더링하지만, 실제 앱에서는 슬라이드 위에 여러 오브젝트가 공존한다.

  • 각 도형을 오프스크린 canvas/framebuffer에 렌더링 후 메인 canvas에 합성
  • 또는 하나의 WebGL context에서 여러 도형을 batch 렌더링
  • Z-order, 클리핑, 그룹 등 기존 렌더링 시스템과의 조합

3. 편집 시스템 결합

현재는 핸들 드래그가 직접 렌더링을 호출하지만, 실제 앱에서는 문서 모델 중심의 편집 흐름을 따라야 한다.

  • 핸들 조작 → Command/Action 발행 → Document Model 갱신 → 렌더러 반응
  • Undo/Redo 스택에 path gradation 관련 변경 기록
  • 다중 선택, 정렬, 그룹핑 등 기존 편집 기능과의 호환

4. 성능 최적화

현재는 도형 1개에 300 vertices, 매 프레임 SDF 재계산이지만, 실제 앱에서는 슬라이드당 수십 개 도형이 있을 수 있다.

  • 정적 도형의 SDF 결과를 텍스처로 캐싱 (도형 변경 시에만 재계산)
  • computeMaxInteriorDistance 결과 캐싱
  • 편집 중인 도형만 실시간 재계산, 나머지는 캐시된 텍스처 사용
  • vertex 수 적응적 조절 (단순 도형은 적게, 복잡한 곡선은 많게)

재사용 가능한 부분

모듈 재사용 가능 여부
shaders.js (SDF fragment shader) 그대로 사용
sdf.js (CPU측 maxDist 계산) 그대로 사용
path-utils.js (SVG path → polygon) 그대로 사용
handles.js (편집 핸들) 앱의 편집 시스템에 맞게 재작성
main.js (WebGL 셋업, 렌더링) 앱의 렌더 파이프라인에 맞게 재작성

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors