- 텍스트(측점/POI) 전 프레임 사전 계산 Map (requestIdleCallback 백그라운드) - 드론 데이터 이동 평균 스무딩 (smoothFrame ±N프레임) - 30fps→60fps 프레임 간 선형 보간 (performance.now() 기반) - EMA(지수이동평균) 표시 위치 스무딩 (α=0.01 기본값) - 글씨 2배 크기, bold, strokeText 테두리, 배경 박스 제거 - 카메라 파라미터 패널에 smooth/EMA α 슬라이더 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.9 KiB
Markdown
53 lines
1.9 KiB
Markdown
# StationOverlay 렌더링 최적화 — 텍스트 스무딩
|
||
|
||
**소요 시간**: 약 90분
|
||
**Context 사용량**: input ~40k / output ~12k tokens
|
||
**이슈**: 없음
|
||
|
||
---
|
||
|
||
## 작업 내용
|
||
|
||
### 문제
|
||
- 측점/지장물 텍스트 라벨이 영상 재생 중 뚝뚝 끊겨 표시됨
|
||
- 드론 pitch/GPS 노이즈 + 30fps 데이터 → 60fps RAF 스텝 차이
|
||
|
||
### 해결 과정
|
||
|
||
1. **원인 분석**
|
||
- `renderCacheRef` useEffect에서 CL 500개 × toCameraCoords = 매 33ms 메인 스레드 블로킹
|
||
- 텍스트는 31개로 작은 부분이었음, CL이 실제 병목
|
||
- 디버그 패널 추가로 firstY 값이 5-7px 불규칙 점프 확인
|
||
|
||
2. **텍스트 사전 계산 Map 도입**
|
||
- `labelMapRef: Map<frameNum, {stationLabels, poiMarkers}>` 전 프레임 precompute
|
||
- `requestIdleCallback` 백그라운드 계산 (메인 스레드 비블로킹)
|
||
- RAF: `Map.get(frameNum)` O(1) 조회만
|
||
|
||
3. **데이터 스무딩 (smoothFrame)**
|
||
- ±N 프레임 이동 평균 (yaw는 sin/cos 평균 후 atan2)
|
||
- UI 슬라이더로 조절 가능 (0~60fr)
|
||
- 기본값: smooth=10
|
||
|
||
4. **30fps→60fps 보간 (frac interpolation)**
|
||
- `performance.now()`로 prop 업데이트 이후 경과 시간 추정
|
||
- 현재/다음 프레임 사이 선형 보간 → 스텝 제거
|
||
|
||
5. **EMA (지수 이동 평균) 표시 위치 스무딩**
|
||
- `displayedStRef`, `displayedPoiRef`: 각 라벨별 현재 표시 위치 유지
|
||
- RAF마다: `pos = prev + (target - prev) × α`
|
||
- UI 슬라이더로 α 조절 (0.01~1.0), 기본값: α=0.01
|
||
- α=0.01: 매우 부드럽지만 약간 뒤처짐
|
||
|
||
6. **시각 개선**
|
||
- 글씨 크기 2배 (9px→18px, 10px→20px)
|
||
- bold + strokeText 4px 검정 테두리 (배경 박스 제거)
|
||
- CL 중심선 재활성화
|
||
|
||
### 최종 기본값
|
||
- smoothHalf: 10 (±333ms 이동 평균)
|
||
- emaAlpha: 0.01 (~1600ms lag, 매우 부드러움)
|
||
|
||
## 산출물
|
||
- `client/src/components/overlay/StationOverlay.tsx` 전면 개편
|