리노컷 공통 파이프라인 캐스트 확장 — 정렬·정규화·얼굴 해칭·원화 눈썹·눈 실측
- register_edit(편집본 ECC 정렬·챔퍼 지표), normalize_base(P1 머리 크기 정규화, 위쪽 종이색 채움) - faceDetail region=face(얼굴 해칭을 원화 픽셀로 보존)·matchFacelessColor - brow_sprite 단계: 원화 눈썹 잉크 스프라이트(hairFront 아래 제외), 윗눈꺼풀 두께·홍채 실측 - 리그 계약 RigBrow.halfWidth/sprite·RigEye.lineScale, 렌더러 눈썹 16띠·바깥쪽 눈꺼풀 굵기 - 실행기 UTF-8 하위 실행, brow_centerline 패딩본 자체 생성, QA 시트 촬영 도구(깜빡임 회피·고해상도 대조) - P1은 새 필드를 모두 끄고 게시 자산·리그 바이트 동일
This commit is contained in:
parent
9a22d1647b
commit
98577cf4ae
18 changed files with 2524 additions and 85 deletions
|
|
@ -16,11 +16,15 @@
|
|||
3. `segmentation.py` — `layers/{body,head,hairFront}.png`(v1) + manifest 기준 섹션
|
||||
4. `layers_v2.py` — `layers/v2/{body,head,hairFront}.png`(턱 밑 띠·잔머리 halo 보정)
|
||||
5. `face_detail.py` — `layers/v2/face-detail.png`
|
||||
6. `paper_grain.py` — `layers/v2/paper-grain.png`(다른 단계와 독립, 순서 유연)
|
||||
7. `lip_texture.py` — `layers/v2/lip-{upper,lower,shadow}.png`
|
||||
8. `jaw_pieces.py` — `layers/v2/jaw-{head,detail}.png`
|
||||
9. `export_rig.py` — WebP 게시(`apps/web/public/avatar/v3/<publicSlug>/`) + `rigs/<rigFileName>` 생성
|
||||
10. `final_previews.py` — 게시된 WebP로 모션·눈/입 확대 미리보기
|
||||
6. `brow_sprite.py` — `layers/v2/brow-{left,right}.png`(원화 눈썹 스프라이트) +
|
||||
`manifest.eyeLineScale`(눈꺼풀 선 굵기 배율) + `manifest.iris`(홍채 실측, 플래그와
|
||||
무관하게 항상 기록). 아래 "`browSpriteFromArt`/`eyeLineScaleFromArt`/
|
||||
`irisFromArt`" 절 참고.
|
||||
7. `paper_grain.py` — `layers/v2/paper-grain.png`(다른 단계와 독립, 순서 유연)
|
||||
8. `lip_texture.py` — `layers/v2/lip-{upper,lower,shadow}.png`
|
||||
9. `jaw_pieces.py` — `layers/v2/jaw-{head,detail}.png`
|
||||
10. `export_rig.py` — WebP 게시(`apps/web/public/avatar/v3/<publicSlug>/`) + `rigs/<rigFileName>` 생성
|
||||
11. `final_previews.py` — 게시된 WebP로 모션·눈/입 확대 미리보기
|
||||
|
||||
이 순서는 전달받은 작업 설명의 번호(주제별 묶음)와 다르다 — 특히 `final_previews`는
|
||||
`export_rig`가 쓴 `export-rig-report.json`을 읽으므로 반드시 `export_rig` *다음*이어야
|
||||
|
|
@ -79,7 +83,17 @@
|
|||
|
||||
"faceDetail": {
|
||||
// 점(기미) 등 랜드마크로 안 나오는 얼굴 반점. 없으면 빈 배열(점 없는 캐릭터도 된다).
|
||||
"moles": [{ "center": [661.3, 627.9], "radius": 20.0 }]
|
||||
"moles": [{ "center": [661.3, 627.9], "radius": 20.0 }],
|
||||
// "features"(기본)면 눈·눈썹·입 주변 타원만 faceDetail(원화 픽셀) 범위다. "face"면
|
||||
// 얼굴 오벌 전체(moles 포함)로 넓어진다 — 캐스트 확장(새 페르소나)은 "face"를 쓴다.
|
||||
// 자세한 설계는 아래 "faceDetail.region" 절 참고.
|
||||
"region": "face",
|
||||
// region="face"일 때만 쓴다. 얼굴 오벌 경계에서 안쪽으로 줄이는 폭(px, 기본 8) —
|
||||
// 원화 윤곽 잉크선이 faceDetail 범위 밖에 남도록 원화를 보고 정한다.
|
||||
"faceInsetPx": 8,
|
||||
// true면 제외 영역(눈·눈썹·입) 안을 채우는 faceless(F) 픽셀의 색을 주변 원화
|
||||
// 피부에 맞춰 평행이동한다(기본 false). 아래 "faceDetail.matchFacelessColor" 절 참고.
|
||||
"matchFacelessColor": true
|
||||
},
|
||||
|
||||
// 렌더러 회전/스케일 중심점(결정문 §8.4). 원화를 보고 목·몸통·얼굴 중심을 정한다.
|
||||
|
|
@ -120,6 +134,150 @@ P1의 기존 `face-detail.png`·`jaw-detail.png`·해당 webp·`p1Rig.ts`는 눈
|
|||
그 고정본과 바이트 단위로 같아지게 한다. **새 페르소나는 이 필드를 넣지 않는다** —
|
||||
처음부터 중심선 보정 좌표로 빌드되므로 과거 좌표를 따로 고정할 이유가 없다.
|
||||
|
||||
### `browHalfWidthFromArt` — 눈썹 굵기를 원화에서 잴지 여부(기본 true, 캐스트 확장)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"browHalfWidthFromArt": false
|
||||
}
|
||||
```
|
||||
|
||||
렌더러(`v3/linocutGeometry.ts` buildBrow)의 눈썹 반두께는 기본으로 P1 설계값
|
||||
(`BROW_STOPS` = [4.5, 3.2, 1.0])이 아니라 **원화에서 실측한 값**을 쓴다
|
||||
(`brow_centerline.py`가 눈썹 잉크 띠의 중심선 법선 방향 반두께를 안쪽·꼭짓점·바깥
|
||||
세 점에서 재 `manifest.browCenterline.halfWidth{Left,Right}`에 남기고,
|
||||
`export_rig.py`가 `browHalfWidthFromArt`가 `false`가 아니면(필드 생략 시 기본 true)
|
||||
그 값을 `rig.landmarks.browLeft/Right.halfWidth`로 내보낸다). 굵은 눈썹 캐릭터(성인·
|
||||
남성 등)가 P1 고정 굵기로 그려져 원화와 달라지는 문제를 캐스트 확장에서 없앤다.
|
||||
|
||||
P1은 `false`로 둔다 — 기존 `p1Rig.ts`가 2단계-B에서 소유자 검수를 거쳐 고정됐으므로
|
||||
(§8.4 눈썹 항목) 재실행해도 바뀌면 안 된다. 측정값 자체는 `browHalfWidthFromArt`
|
||||
설정과 무관하게 항상 manifest에 남는다(참고용). **새 페르소나는 이 필드를 생략한다**
|
||||
(기본 true로 원화 실측값을 쓴다).
|
||||
|
||||
### `browSpriteFromArt`/`eyeLineScaleFromArt`/`irisFromArt` — 눈썹 스프라이트·눈꺼풀 굵기·홍채(모두 기본 true)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"browSpriteFromArt": false,
|
||||
"eyeLineScaleFromArt": false,
|
||||
"irisFromArt": false
|
||||
}
|
||||
```
|
||||
|
||||
굵은 판화 눈썹 캐릭터는 벡터 눈썹(P1 설계 굵기에 맞춘 리본·털 획)이 "갈색빛 회색
|
||||
막대"로 그려진다(결정문 §8.4.1). 입술과 같은 "중립 렌더 = 원화" 원칙을 적용한다:
|
||||
|
||||
- `browSpriteFromArt`(기본 true): `brow_sprite.py`가 눈썹 중심선(`manifest.landmarks.
|
||||
eyebrowLeft/Right` + `browCenterline.halfWidth{Left,Right}`)을 따라 띠를 만들고,
|
||||
그 안의 원화 잉크(Otsu+diff 연결성분, `brow_centerline.py`와 같은 기준)에서 머리카락
|
||||
(`layers/v2/hairFront.png` 알파 — 분할 카테고리 자체가 아니다. 이 화풍에서는
|
||||
mediapipe 머리카락 분할이 두꺼운 눈썹 잉크 자체를 머리카락으로 오탐해, 분할 결과를
|
||||
그대로 빼면 눈썹 잉크 태반이 사라진다. 실제로 화면에서 눈썹 위에 겹쳐 그려질
|
||||
hairFront 알파로 기준을 좁혀야 "앞머리 가닥이 눈썹과 같이 움직이면 안 된다"는
|
||||
의도를 과하게 지우지 않고 구현할 수 있다)를 뺀 자리만 `brow-{left,right}.png`로
|
||||
뗀다. `export_rig.py`가 입술 결과 같은 무손실(exact) WebP로 게시하고
|
||||
`rig.landmarks.browLeft/Right.sprite`를 채운다 — 렌더러(`linocutGeometry.ts`
|
||||
`computeBrowSpriteStripTransforms`)가 벡터 몸통·털 획 대신 이 스프라이트를 세로
|
||||
16띠로 나눠 옮긴다(안쪽 올림 그늘은 그대로 벡터로 그린다).
|
||||
- `eyeLineScaleFromArt`(기본 true): `brow_sprite.py`가 눈 폭 가운데 60% 열마다
|
||||
`upperLidTop.y-12`~홍채 중심 y 사이 첫 연속 어두운(lum<80) 구간 길이의 중앙값을
|
||||
재, P1 기준값(2.0px, 상수로 고정 — 근거는 `brow_sprite.py` 주석 참고)으로 나눈
|
||||
비를 `[1.0, 2.5]`로 clamp한 값을 `manifest.eyeLineScale`에 남긴다. `export_rig.py`가
|
||||
`rig.landmarks.eyeLeft/Right.lineScale`로 내보내고, 렌더러가 눈꺼풀·쌍꺼풀·
|
||||
아랫눈꺼풀 굵기에 곱한다(홍채·흰자는 그대로).
|
||||
|
||||
- `irisFromArt`(기본 true): mediapipe iris 랜드마크(468~477)는 실제 사람 눈 비례로
|
||||
학습돼, 판화 잉크로 그려진 홍채보다 반지름을 크게(또는 중심을 다르게) 잡을 수 있다
|
||||
(2차 보정 T3, P7 실측: 가로 반지름 기준 리그 대비 약 11~15% 작다). `brow_sprite.py`가
|
||||
(browSpriteFromArt/eyeLineScaleFromArt와 무관하게 항상) 원화에서 홍채 가로 반지름·
|
||||
중심을 재 `manifest.iris`에 남기고, `export_rig.py`가 `irisFromArt`가 `false`가
|
||||
아니면 `rig.landmarks.eyeLeft/Right.iris`를 이 실측값으로 채운다(세로 경계는 윗눈꺼풀
|
||||
잉크와 홍채가 같은 톤으로 이어져 가를 수 없어, 가로 반지름 + 아래쪽 경계로만
|
||||
중심y를 역산한다 — 자세한 방법은 `brow_sprite.py` `measure_iris` 참고).
|
||||
|
||||
P1은 셋 다 `false`다 — 이미 고정 벡터 눈썹·굵기·홍채 위치로 빌드·검수됐고,
|
||||
`eyeLineScaleFromArt`의 경우 P1 자체가 lineScale의 기준값이라 바뀌면 안 된다.
|
||||
**새 페르소나는 이 필드들을 생략한다**(기본 true로 원화 스프라이트·실측 굵기·실측
|
||||
홍채를 쓴다).
|
||||
|
||||
### `faceDetail.region` — faceDetail이 그려질 범위(기본 "features", 캐스트 확장 "face")
|
||||
|
||||
`face_detail.py`가 눈·눈썹·입 "제외 영역"(이목구비 잉크선) 밖에서 원화(base-front) 픽셀을
|
||||
쓰는 범위를 정한다. 레이어 픽셀은 항상 base-faceless(F)에서 가져오므로(README 상단
|
||||
단계 설명 참고), 이 범위 밖은 F뿐이라 — 다크서클·볼 해칭·팔자 주름·턱 그늘 같은
|
||||
원화 고유의 판화 해칭이 전혀 남지 않는다.
|
||||
|
||||
- `"features"`(기본, P1 그대로): 눈·입 주변 타원(눈두덩~다크서클, 입가 음영)에서만
|
||||
원화 픽셀을 쓴다. 나머지 얼굴 피부는 F(매끈한 기본형)다.
|
||||
- `"face"`(캐스트 확장): 얼굴 오벌(export_rig.compute_face_oval, 다른 단계와 같은
|
||||
36점 루프·1.04 배율) 전체에서 원화 픽셀을 쓴다. moles 타원은 그대로 합쳐진다.
|
||||
- 오벌을 그대로 쓰면 원화 얼굴 윤곽 잉크선(jaw_pieces.py 기준 오벌 경계 ±18px
|
||||
안팎)이 영역 안에 들어와, F 기준으로 이미 그려진 head 레이어의 윤곽선과 겹쳐
|
||||
두 겹 턱선(머리 이동 시차)이 생긴다 — `faceInsetPx`(기본 8)만큼 오벌을 안쪽으로
|
||||
줄여(거리변환 기준, 방사형 스케일이 아니다) 이 잉크선을 영역 밖에 둔다. 페더는
|
||||
다른 영역과 같은 `REGION_FEATHER_PX`(12px, sigma 6)를 쓴다. 새 페르소나마다
|
||||
원화 윤곽선 위치가 다르므로, 원화를 보고 `faceInsetPx`를 정해 보고해야 한다.
|
||||
- 머리카락 알파 0 규칙(머리카락 위에는 faceDetail을 그리지 않는다)은 그대로이되,
|
||||
`"face"`에서는 머리카락 마스크를 **원화(front)와 faceless(F) 분할 결과의
|
||||
합집합**으로 만든다. 둘의 앞머리 가닥 위치가 달라(codex 편집본 시차) 한쪽
|
||||
분할만 쓰면 다른 쪽 잔머리가 마스크 밖에 남아 유령 가닥이 생기기 때문이다.
|
||||
`"features"`는 기존처럼 F 분할만 쓴다(P1 회귀 보존).
|
||||
|
||||
manifest.layersV2.faceDetail.region/faceInsetPx에 남는다.
|
||||
|
||||
### `faceDetail.matchFacelessColor` — 제외 영역 F 색을 원화 피부에 맞추기(기본 false)
|
||||
|
||||
`true`면 제외 영역(눈·눈썹·입) 안을 채우는 F 픽셀의 색을 주변 원화 피부에 맞춘다.
|
||||
영역(eyeLeft/eyeRight/browLeft/browRight/mouth)마다 그 영역 바깥 `RING_PX`(12px)
|
||||
고리에서 어두운 잉크(원화 명도<110) 픽셀을 뺀 나머지로 원화·F 평균 RGB 차(ΔRGB)를
|
||||
구하고, 그 영역 전체의 F 픽셀을 ΔRGB만큼 그대로 평행이동(가산)한다 — 보간·블러·
|
||||
패치 합성은 하지 않는다(과거 P1에서 그 방식이 회색 얼룩으로 반려된 적이 있다).
|
||||
영역별 ΔRGB는 콘솔과 `manifest.layersV2.faceDetail.matchFacelessColorDeltaRgb`에
|
||||
남는다. P1은 `false`(기존 결과 그대로)다.
|
||||
|
||||
## 캐스트 확장 0단계 — 원화 3장을 준비하는 공통 도구
|
||||
|
||||
새 페르소나의 후보 원화는 크기·구도가 P1과 다르다(결정문 §8.2 생성 규칙은 그대로
|
||||
따르되, 캔버스에 맞추는 과정이 필요하다). 아래 두 도구가 "사람이 고른 후보 원화 +
|
||||
codex 편집본 2장"을 파이프라인이 기대하는 `base/base-front.png`·
|
||||
`base/base-faceless.png`·`raw/body.png`(모두 캔버스 1005x1566)로 만든다. 순서대로
|
||||
쓴다:
|
||||
|
||||
1. `register_edit.py` — codex가 만든 얼굴 없는 기본형·body 편집본을 ECC 아핀
|
||||
정렬(`cv2.findTransformECC`, `MOTION_AFFINE`)로 후보 원화(ref) 좌표계에 맞춘다.
|
||||
edited가 ref와 크기가 달라도 된다(먼저 ref 크기로 리사이즈한다).
|
||||
|
||||
```bash
|
||||
<venv>/python.exe register_edit.py <ref.png> <faceless-edit.png> <faceless-aligned.png> \
|
||||
--mode faceless --report <faceless-report.json>
|
||||
<venv>/python.exe register_edit.py <ref.png> <body-edit.png> <body-aligned.png> \
|
||||
--mode body --report <body-report.json>
|
||||
```
|
||||
|
||||
콘솔·리포트 JSON에 정렬 품질 지표(변환 배율·회전·이동, 마스크 안 Canny 에지
|
||||
챔퍼 거리 중앙값·p90, faceless는 얼굴 윤곽 띠 챔퍼와 눈·눈썹·입 상자 잉크 비율)를
|
||||
낸다 — **판정은 하지 않는다**(수용 기준은 출력만, 사람/오케스트레이터가 본다).
|
||||
|
||||
2. `normalize_base.py` — 위 세 장(원래 ref, 정렬된 faceless, 정렬된 body)에 **같은**
|
||||
아핀 변환 하나(등방 배율 + 평행이동, 회전 없음)를 적용해 캔버스로 옮긴다. 배율·
|
||||
이동은 P1 `base-front.png`의 눈 중심·눈 사이 거리·눈-턱 거리 비율에 맞춘다
|
||||
(P1과 머리 크기 비율이 같아지도록).
|
||||
|
||||
```bash
|
||||
<venv>/python.exe normalize_base.py <persona-dir> \
|
||||
--front <ref.png> --faceless <faceless-aligned.png> --body <body-aligned.png>
|
||||
```
|
||||
|
||||
출력: `<persona-dir>/base/base-front.png`·`base/base-faceless.png`(1005x1566
|
||||
RGB, 위아래 빈 곳은 원본 가장자리 띠 반사 패딩)·`raw/body.png`(1005x1566,
|
||||
빈 곳은 `#00ff00`)·`base/normalize.json`(배율·오프셋·덮인 범위·입력 파일
|
||||
sha256).
|
||||
|
||||
이 둘은 `persona.json`이 아직 없어도 돈다(두 스크립트 다 페르소나 폴더 경로만
|
||||
받고 `load_persona_config`에 의존하지 않는다) — persona.json은 이 산출물을 보고
|
||||
그다음에 쓴다(아래 "새 페르소나를 추가할 때" 3번).
|
||||
|
||||
## 모델 파일(저장소에 없음)
|
||||
|
||||
`scripts/_models/`에 MediaPipe 모델을 받아 둔다(모든 페르소나가 공유, `.gitignore`의
|
||||
|
|
@ -137,8 +295,12 @@ python 환경은 numpy·Pillow·scipy·opencv·mediapipe가 필요하다.
|
|||
|
||||
1. 원화 2장: `<persona-dir>/base/base-front.png`(정면 기본형)과
|
||||
`base-faceless.png`(같은 그림에서 눈·눈썹·입만 지운 것). 결정문 §8.2 생성 규칙을 따른다.
|
||||
후보 원화 크기·구도가 P1과 다르면(캐스트 확장이 보통 그렇다) 바로 두지 말고
|
||||
위 "캐스트 확장 0단계" 도구(`register_edit.py` → `normalize_base.py`)로 캔버스에
|
||||
맞춰 만든다.
|
||||
2. `<persona-dir>/raw/body.png`: body 레이어가 head_mask로 가려지는 목 상단 영역을
|
||||
메울 재생성 참조 편집본(크로마키 초록 배경, base-front와 같은 정렬).
|
||||
메울 재생성 참조 편집본(크로마키 초록 배경, base-front와 같은 정렬). 역시 캐스트
|
||||
확장이면 위 도구가 만든다.
|
||||
3. `<persona-dir>/persona.json`: 위 스키마대로 작성한다. 특히 `styleFrame`·
|
||||
`paletteSamples`·`pivots`는 원화를 눈으로 보고 정해야 한다(자동 유도 불가).
|
||||
4. (선택) `<persona-dir>/motif/`: 모티프 스프라이트는 이 라운드 범위 밖이다 — 오케스트레이터가
|
||||
|
|
@ -147,3 +309,45 @@ python 환경은 numpy·Pillow·scipy·opencv·mediapipe가 필요하다.
|
|||
|
||||
그 다음 `run_pipeline.py <persona-dir>`를 실행하고, 출력된 검사 수치(halo%, 평균절대차
|
||||
등 — 각 단계 스크립트가 콘솔에 찍고 manifest.json에도 남긴다)를 기준치와 비교해 판정한다.
|
||||
|
||||
## QA 캡처(`qa/capture_cast_sheet.mjs`)
|
||||
|
||||
리그가 게시된(`run_pipeline.py`가 `export_rig.py`까지 끝낸) 페르소나를 Lab
|
||||
(`/dev/avatar-lab`)에서 조작해 캐스트 시트 5장을 만든다.
|
||||
|
||||
```bash
|
||||
node qa/capture_cast_sheet.mjs <CODE> <outDir> [--base http://localhost:5173]
|
||||
```
|
||||
|
||||
- `<CODE>`: `P1`처럼 대문자 페르소나 코드. `apps/web/src/components/avatar/v3/rigs/<code>Rig.ts`의
|
||||
`<CODE>_LINOCUT_RIG` export와 `docs/avatar-art/<code>-linocut/base/base-front.png`를 읽는다.
|
||||
- Lab에 `?persona=<CODE>` 쿼리(`v3/rigs/index.ts` `labLinocutRigs()` + `AvatarLab.tsx`
|
||||
`?persona=` 지원, 세션 화면과 무관)로 들어가 그 페르소나를 바로 고른다.
|
||||
- `--base`가 가리키는 dev server가 이미 떠 있어야 한다(이 스크립트는 서버를
|
||||
띄우거나 끄지 않는다). 기본값 `http://localhost:5173`.
|
||||
|
||||
산출(`<outDir>`에 JPG, 모두 긴 변 ≤1000px — 개별 캡처 PNG는 `<outDir>/frames/`):
|
||||
|
||||
- `sheet-expr-bust.jpg` / `sheet-expr-face.jpg`: 16표정(강도 1) × bust/face 크롭,
|
||||
4×4 격자.
|
||||
- `sheet-viseme.jpg`: 비짐 9종(X·M·A·EO·O·U·EU·I·E), 입 주변 확대, 3×3 격자. Lab에
|
||||
채널을 직접 주입하는 고정 기능이 없어(엔진 값을 바꾸지 않는 이 패킷 범위에서는
|
||||
추가하지 않는다) 비짐 9종이 모두 나오는 문장을 재생하며 50ms 간격으로
|
||||
(시각·비짐·스크린샷)을 한 패스에 모아 두고, 각 비짐이 가장 오래 유지된 구간의
|
||||
중앙 표본을 그 비짐의 대표 프레임으로 쓴다(스캔과 재생을 두 패스로 나눠 같은
|
||||
시각을 다시 재생하면 클릭~엔진 시계 리셋 사이 수십ms급 비결정성 때문에 스캔 때와
|
||||
다른 비짐이 잡힐 수 있음을 실측으로 확인했다 — 항상 한 패스로 찍는다).
|
||||
`<outDir>/viseme-timeline.json`에 전체 표본을 남긴다.
|
||||
- `neutral-vs-art.jpg`: neutral 렌더(portrait 크롭)와 `base/base-front.png`를
|
||||
나란히 둔 것 — 리그 피벗·배율이 원화와 맞는지 한눈에 비교한다.
|
||||
- `face-art-vs-render.jpg`: 고해상도 얼굴 대조(2차 보정 T4b). `rig.crops.face`
|
||||
영역을 원화는 500px, 렌더(neutral·grief·joy·angry)는 760px로 키워 2줄 3칸으로
|
||||
나란히 두고 최종 긴 변 ≤1000px로 다시 줄인다 — 눈·눈썹처럼 작은 차이가 bust
|
||||
전체 비교에서는 묻히는 부위를 본다.
|
||||
|
||||
**깜빡임/유휴 시선 흔들림 회피**(`screenshotEyeStable`, 2차 보정 T4a): `neutral-vs-art.jpg`·
|
||||
`face-art-vs-render.jpg`의 정적 비교 스크린샷은 Reduced motion을 다시 켠 뒤(viseme
|
||||
구간에서 꺼 둔 채로 남는다 — 유휴층 breath·blink·시선 흔들림이 채널에 섞여 든다)
|
||||
700ms 간격 3연사 중 eye-left outline(흰자 바탕) bbox 높이가 가장 큰 장을 쓴다 —
|
||||
neutral-vs-art가 깜빡임 중에 찍힌 사례(P2)와, 원화 대비 렌더 홍채 아래 흰 띠가
|
||||
유휴 시선 흔들림(gazeY) 중에만 나타나는 것을 실측으로 확인했다(2026-10-01).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue