# Blendshape Gaze Timeline

`SkinnedMeshRenderer`의 블렌드쉐입만으로 캐릭터 시선을 타깃/카메라에 고정하는 시스템입니다. 3×3로 캘리브레이션한 포즈를 yaw/pitch에 따라 보간하며, 재생 중에는 `LateUpdate`에서 기존 페이셜의 eyeLook 채널을 최종 합성합니다.

## 캐릭터 세팅

1. 캐릭터 루트에 `BlendshapeGazeDriver`를 추가합니다.
2. `Target Renderer`에 Face `SkinnedMeshRenderer`를 직접 할당합니다.
3. Driver 인스펙터에서 `Open Guided Calibration`을 누릅니다.
4. 전용 창에서 `Create Profile`, `Sync ARKit EyeLook 8`을 순서대로 누릅니다.
5. `Auto Create Gaze Origin`을 누릅니다. eyeLook 블렌드쉐입이 움직이는 좌우 정점 중심을 사용하며, 계산할 수 없으면 Renderer bounds 중심을 사용합니다.
6. `Face_GazeOrigin`은 Head 하위에 두고 `Align Origin Forward to Scene Camera`로 로컬 +Z를 정면에 맞춥니다. Gaze Origin의 월드 회전이 항상 시선 방향 기준이며, `Head Reference`는 Origin이 없을 때만 대체 기준으로 사용됩니다.

`Target Camera`를 지정하면 타깃이 비어 있는 Timeline Clip이 해당 카메라를 봅니다. 지정하지 않으면 `Camera.main`을 사용합니다.
프로필은 Sync한 Source Mesh를 함께 기억하므로 다른 의상의 Face를 잘못 연결하면 인스펙터에서 경고하고 출력을 중단합니다. 동일한 캘리브레이션을 호환 Mesh에 의도적으로 공유할 때만 Source Mesh를 비웁니다.

## 가이드 캘리브레이션

Driver 인스펙터의 `Open Guided Calibration` 또는 메뉴 `Tools > Streamingle > Gaze Calibration`로 창을 엽니다.

1. 설정 체크가 모두 정상인지 확인하고 `Start Calibration`을 누릅니다. 프로필은 저장되지 않는 작업 복사본에서 편집되고, 이때의 얼굴 값도 안전하게 보관됩니다. 종료하거나 창을 닫거나 씬/프리팹 저장을 시작하면 원래 얼굴 포즈로 복원됩니다.
2. ARKit 표준 eyeLook 8채널이면 `Auto Seed ARKit`으로 기본 포즈를 한 번에 만들 수 있습니다.
3. 패드에서 Center, Left, Right, Up, Down을 선택하고 좌·우 눈 슬라이더로 직접 다듬습니다. 방향 이름은 **캐릭터 기준**입니다.
4. `Generate Corners`로 네 모서리를 자동 생성합니다. 필요한 경우 `Advanced 3x3`을 켜고 모서리를 개별 보정합니다.
5. 9개 포인트가 준비되면 빈 공간을 드래그해 `Live Interpolation` 결과를 확인합니다.
6. 9/9면 `Apply & Finish`, 작업 중간이면 `Save Draft & Finish`로 마칩니다. 프로필은 저장되고, 캘리브레이션 전 얼굴 포즈가 복원됩니다. `Cancel & Revert` 또는 창 닫기는 이번 세션의 프로필 변경까지 되돌립니다.

`Mirror`, `Reset`, `Copy/Paste` 도구로 반복 작업을 줄일 수 있습니다. Corrective 채널은 -100~100을 직접 조절할 수 있습니다.

| Pitch / Yaw | Left (Min Yaw) | Center | Right (Max Yaw) |
| --- | --- | --- | --- |
| Up (Max Pitch) | Up Left | Up | Up Right |
| Center | Left | Center | Right |
| Down (Min Pitch) | Down Left | Down | Down Right |

9개 셀이 모두 준비되어야 런타임 출력이 활성화됩니다. 시야각 밖의 타깃은 가장자리 값으로 고정됩니다. 비대칭 시야각은 전용 창의 `Asymmetric View Limits`에서 설정합니다.

추가 눈꺼풀 보정이 필요하면 프로필 Channels에 블렌드쉐입을 추가하고 Usage를 `Corrective`로 지정한 뒤 9개 샘플을 다시 캡처합니다. 기본 `Additive From Center` 모드는 방향별 값과 Center 값의 차이만 기존 blink 위에 더하므로 페이셜 표정을 보존합니다. 필요하면 채널별로 `Override`를 선택할 수도 있습니다. Timeline Clip의 `Enable Correctives`가 켜진 구간에서만 적용됩니다.

서로 크로스페이드하는 Clip은 `Enable Correctives` 설정을 동일하게 맞추는 것을 권장합니다. 설정이 다르면 교차 구간의 우세 Clip이 바뀌는 시점에 Corrective 사용 여부도 전환됩니다.

## Timeline

1. Timeline에 `Blendshape Gaze Track`을 추가합니다.
2. Track 바인딩에 캐릭터의 `BlendshapeGazeDriver`를 할당합니다.
3. `Blendshape Gaze Clip`을 추가하고 구간과 블렌드 인/아웃을 조정합니다.
4. Clip Target을 비우면 Driver의 Target Camera 또는 Main Camera를 사용합니다. 다른 Transform을 직접 지정할 수도 있습니다.

현재 지원 구성은 `BlendshapeGazeDriver` 하나당 활성 Gaze Track 하나입니다. 여러 Gaze Track을 같은 Driver에 동시에 바인딩하면 영향도가 가장 큰 Track 하나가 선택되므로, 클립 블렌딩은 같은 Track 안에서 구성합니다.

Clip 영향도 1에서는 캘리브레이션 eyeLook 값이 기존 페이셜 값을 완전히 덮고, 블렌드 구간에서는 기존 값과 자연스럽게 보간합니다. 런타임 최종 쓰기는 `LateUpdate`에 수행되므로 Animator와 실시간 페이셜 리시버의 `Update` 출력 뒤에 적용됩니다.

에디터 Timeline 미리보기에서는 Animation Track이 Head/Gaze Origin 회전과 페이셜 블렌드쉐입을 적용한 뒤 예약된 최종 Gaze 패스를 실행합니다. 따라서 스크럽 중에도 현재 모션의 머리 방향을 기준으로 계산하고, 구성된 eyeLook 채널은 해당 프레임의 페이셜 값 위에 마지막으로 합성됩니다.

카메라 컷은 Driver의 `Cut Response`로 `Snap` 또는 `Smooth`를 선택합니다. Clip의 `Override Cut Response`를 켜면 구간별로 다른 정책과 전환 시간을 사용할 수 있습니다. `Cut Detection Angle`이 0이면 같은 카메라 오브젝트의 포즈 점프 감지를 끄며, 타깃 오브젝트 교체는 계속 컷으로 처리됩니다.

### 독쥐 콘서트 씬

열린 `독쥐_260709_콘서트_렌더링` 씬에는 곡마다 `TimeLine/<곡 이름>/Motion` PlayableDirector가 있습니다. Gaze Track은 이 Motion Timeline에 추가합니다. Driver의 Target Renderer는 해당 곡/의상의 `Face_*`를 직접 연결합니다. 예를 들어 Promise Way는 `TimeLine/Promise Way/Character/.../Promiseway/Face_Promiseway`입니다. 다른 곡도 같은 구조의 `Face_밤하늘별`, `Face_단나사마`, `Face_오르트구름`, `Face_파도혁명`을 사용합니다.

## 실시간 확장 API

Timeline 외 입력은 같은 Driver를 그대로 사용할 수 있습니다.

```csharp
driver.SetRuntimeTarget(targetTransform, influence: 1f);
driver.SetRuntimeTargetPosition(worldPosition, influence: 1f);
driver.ClearRuntimeTarget();
```

활성 Timeline 요청이 있으면 Runtime Target보다 우선합니다. `SetRuntimeTarget(null)`은 Driver의 Target Camera를 먼저 사용하고, 지정되지 않았으면 `Camera.main`을 사용합니다.

현재 버전은 AnimationClip 베이크를 만들지 않습니다.
