@convai/embody 1.0.1-beta.0 → 1.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +12 -8
- package/dist/character/createCharacter.d.ts +66 -5
- package/dist/character/createCharacter.js +93 -22
- package/dist/character/pipeline.d.ts +9 -5
- package/dist/clips/animations.d.ts +13 -1
- package/dist/clips/animations.js +28 -6
- package/dist/clips/retarget.d.ts +168 -0
- package/dist/clips/retarget.js +856 -0
- package/dist/clips/sources.d.ts +70 -0
- package/dist/clips/sources.js +119 -0
- package/dist/connect/session.d.ts +11 -0
- package/dist/connect/session.js +22 -0
- package/dist/index.d.ts +22 -1
- package/dist/index.js +12 -0
- package/dist/inspect/inspectModel.d.ts +14 -0
- package/dist/inspect/inspectModel.js +26 -0
- package/dist/inspect/morphMap.d.ts +54 -0
- package/dist/inspect/morphMap.js +140 -0
- package/dist/inspect/report.d.ts +88 -0
- package/dist/inspect/report.js +193 -0
- package/dist/motion/eyeAim.d.ts +94 -0
- package/dist/motion/eyeAim.js +276 -0
- package/dist/motion/eyeGaze.d.ts +17 -0
- package/dist/motion/eyeGaze.js +42 -0
- package/dist/motion/footLock.d.ts +67 -0
- package/dist/motion/footLock.js +176 -0
- package/dist/motion/gaze.d.ts +135 -180
- package/dist/motion/gaze.js +257 -491
- package/dist/motion/hair.d.ts +27 -0
- package/dist/motion/hair.js +127 -0
- package/dist/motion/hairClearance.d.ts +39 -0
- package/dist/motion/hairClearance.js +125 -0
- package/dist/motion/morphNormals.d.ts +38 -0
- package/dist/motion/morphNormals.js +237 -0
- package/dist/motion/types.d.ts +15 -0
- package/dist/react/CharacterModel.d.ts +3 -1
- package/dist/react/CharacterModel.js +9 -1
- package/dist/react/ConvaiCharacter.js +4 -1
- package/dist/react/PortraitLights.d.ts +3 -0
- package/dist/react/PortraitLights.js +44 -0
- package/dist/render/faceMaterials.d.ts +60 -3
- package/dist/render/faceMaterials.js +320 -96
- package/dist/render/portraitRig.d.ts +28 -0
- package/dist/render/portraitRig.js +55 -0
- package/dist/render/surfaces.d.ts +181 -0
- package/dist/render/surfaces.js +1020 -0
- package/dist/rig/resolve.d.ts +8 -2
- package/dist/rig/resolve.js +7 -3
- package/dist/rig/units.d.ts +35 -16
- package/dist/rig/units.js +143 -42
- package/dist/speech/expression.d.ts +20 -0
- package/dist/speech/expression.js +118 -0
- package/dist/speech/lipsync.d.ts +68 -2
- package/dist/speech/lipsync.js +205 -5
- package/dist/speech/mouthFinish.d.ts +46 -0
- package/dist/speech/mouthFinish.js +235 -0
- package/dist/speech/voiceClock.d.ts +94 -0
- package/dist/speech/voiceClock.js +176 -0
- package/package.json +1 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A portrait light rig for a character, framework-free: what
|
|
3
|
+
* `ConvaiCharacter` lights its own canvas with, for apps on plain three.js.
|
|
4
|
+
* After the coffee-shop studio's film rig: a soft-shadowed key 45° to screen
|
|
5
|
+
* left of the camera and 30° up, following the camera round the character
|
|
6
|
+
* and never more than 70° off its front (a turn to its darker side is not a
|
|
7
|
+
* black profile); a fill at about a ninth of the key, a warm rim, a dim sky.
|
|
8
|
+
* Pair it with `PORTRAIT_TONE` (ACES pushed skin orange) and a dim
|
|
9
|
+
* environment, and call `placePortraitRig` every frame.
|
|
10
|
+
*/
|
|
11
|
+
import * as THREE from 'three';
|
|
12
|
+
/** Tone mapping the rig was tuned under. */
|
|
13
|
+
export const PORTRAIT_TONE = { toneMapping: THREE.NeutralToneMapping, exposure: 1.05 };
|
|
14
|
+
/** Where the key sits from the lens, and how far from the character's front it may go, degrees. */
|
|
15
|
+
const KEY_FROM_LENS = -45;
|
|
16
|
+
const KEY_LIMIT = 70;
|
|
17
|
+
/** Unit direction at an azimuth (from +Z, the character's front) and elevation, degrees. */
|
|
18
|
+
const toward = (az, el, out) => {
|
|
19
|
+
const a = THREE.MathUtils.degToRad(az), e = THREE.MathUtils.degToRad(el);
|
|
20
|
+
return out.set(Math.sin(a) * Math.cos(e), Math.sin(e), Math.cos(a) * Math.cos(e));
|
|
21
|
+
};
|
|
22
|
+
/** Where the key goes for a camera at `camAz` degrees round the character. */
|
|
23
|
+
export function keyAzimuth(camAz) {
|
|
24
|
+
return THREE.MathUtils.clamp(camAz + KEY_FROM_LENS, -KEY_LIMIT, KEY_LIMIT);
|
|
25
|
+
}
|
|
26
|
+
/** The rig's lights: a soft-shadowed key, a fill, a warm rim, a dim sky. */
|
|
27
|
+
export function createPortraitRig() {
|
|
28
|
+
const key = new THREE.DirectionalLight(0xfff4ea, 3.2);
|
|
29
|
+
key.castShadow = true;
|
|
30
|
+
key.shadow.mapSize.set(2048, 2048);
|
|
31
|
+
const cam = key.shadow.camera;
|
|
32
|
+
cam.left = cam.bottom = -1.1;
|
|
33
|
+
cam.right = cam.top = 1.1;
|
|
34
|
+
cam.near = 0.1;
|
|
35
|
+
cam.far = 10;
|
|
36
|
+
key.shadow.bias = -0.0002;
|
|
37
|
+
key.shadow.normalBias = 0.003;
|
|
38
|
+
key.shadow.radius = 3;
|
|
39
|
+
const fill = new THREE.DirectionalLight(0xe8ecff, 0.36);
|
|
40
|
+
const rim = new THREE.DirectionalLight(0xffd8b4, 1.6);
|
|
41
|
+
const sky = new THREE.HemisphereLight(0xffffff, 0x3a3530, 0.25);
|
|
42
|
+
return { key, fill, rim, sky };
|
|
43
|
+
}
|
|
44
|
+
const dir = new THREE.Vector3();
|
|
45
|
+
/** Place the rig for a camera at `camera`, lighting the point `at`. */
|
|
46
|
+
export function placePortraitRig(rig, camera, at) {
|
|
47
|
+
const keyAz = keyAzimuth(THREE.MathUtils.radToDeg(Math.atan2(camera.x - at.x, camera.z - at.z)));
|
|
48
|
+
rig.key.position.copy(at).addScaledVector(toward(keyAz, 30, dir), 4);
|
|
49
|
+
rig.fill.position.copy(at).addScaledVector(toward(keyAz - 115, 20, dir), 4);
|
|
50
|
+
rig.rim.position.copy(at).addScaledVector(toward(keyAz + 150, 35, dir), 4);
|
|
51
|
+
for (const l of [rig.key, rig.fill, rig.rim]) {
|
|
52
|
+
l.target.position.copy(at);
|
|
53
|
+
l.target.updateMatrixWorld();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface shading for skin and hair — how the body takes whatever light the
|
|
3
|
+
* app puts it in.
|
|
4
|
+
*
|
|
5
|
+
* Lighting is the app's. What embody owns is that the materials respond to
|
|
6
|
+
* it the way skin and hair do, under a portrait key, noon sun, a neon club or
|
|
7
|
+
* an overcast sky alike. The convai-mha-1 exports ship neither: the face is a
|
|
8
|
+
* plain standard material (metalness map and all) whose light/shadow edge cuts
|
|
9
|
+
* straight from lit to black, and the hair is roughness 1, IOR 1.1 chalk drawn
|
|
10
|
+
* as a depthless blended shell over hard-cut cards.
|
|
11
|
+
*
|
|
12
|
+
* What changes, and on which renderer:
|
|
13
|
+
*
|
|
14
|
+
* EVERY RENDERER (material properties three's WebGL and WebGPU renderers
|
|
15
|
+
* both honour):
|
|
16
|
+
* · skin → MeshPhysicalMaterial: skin's IOR (1.4) and specular, no metal,
|
|
17
|
+
* and a faint warm sheen at grazing angles where light scatters out;
|
|
18
|
+
* · hair cards and lashes → alpha-to-coverage in the opaque pass: they write
|
|
19
|
+
* depth (so they sort and take shadows) and MSAA softens their edges; the
|
|
20
|
+
* export's blended hair layer stays blended, after the cards, as the soft
|
|
21
|
+
* edge; hair gets a broad, soft keratin sheen in its own colour;
|
|
22
|
+
* · the scalp under the hair is shaded as skin in the hair's shade (vertex
|
|
23
|
+
* colours), so partings and the gaps between cards don't show bare skin;
|
|
24
|
+
* · every texture is sampled anisotropically.
|
|
25
|
+
*
|
|
26
|
+
* WEBGL ONLY (a shader patch WebGPU ignores, leaving the above):
|
|
27
|
+
* · light wraps past the terminator as a warm band, as it does through skin
|
|
28
|
+
* (Green, "Real-time approximations to subsurface scattering", GPU Gems 1
|
|
29
|
+
* ch.16), so the edge is soft instead of clay;
|
|
30
|
+
* · thin tissue (ears, nostril wings) glows when lit from behind
|
|
31
|
+
* (Barré-Brisebois & Bouchard, GDC 2011);
|
|
32
|
+
* · a fine pore normal over the face's own normal map — the published face
|
|
33
|
+
* textures are 1024², too coarse to hold pores under a hard key.
|
|
34
|
+
*
|
|
35
|
+
* Values are the ones the coffee-shop studio arrived at by comparing renders
|
|
36
|
+
* against Unreal MetaHuman references under moving light.
|
|
37
|
+
*/
|
|
38
|
+
import * as THREE from 'three';
|
|
39
|
+
export interface SkinTuning {
|
|
40
|
+
/** Strength of the light that wraps past the terminator, 0–1. Default 0.45. */
|
|
41
|
+
subsurface?: number;
|
|
42
|
+
/** How far past the terminator light reaches (wrap), 0–1. Default 0.65. */
|
|
43
|
+
wrap?: number;
|
|
44
|
+
/** Backlit glow through thin tissue, 0–1. Default 0.3. */
|
|
45
|
+
translucency?: number;
|
|
46
|
+
/** Pore detail strength, 0 to disable. Default 0.16. */
|
|
47
|
+
detail?: number;
|
|
48
|
+
/** Pore detail repeats across the face texture. Default 30. */
|
|
49
|
+
detailTile?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Light skin keeps in shadow, linear RGB: warm, as skin in shade glows
|
|
52
|
+
* from light carried under it, and no more saturated than that
|
|
53
|
+
* (on darker skin a strong red shade read as a bruise). Default [0.12, 0.07, 0.055].
|
|
54
|
+
*/
|
|
55
|
+
shadowFloor?: [number, number, number];
|
|
56
|
+
/** Narrowest a shadow edge on skin is filtered, in shadow-map texels (PCF). Default 3. */
|
|
57
|
+
shadowSoftness?: number;
|
|
58
|
+
/** Roughness, where the skin has no roughness map. Default 0.52; 0.48 on a male record. */
|
|
59
|
+
roughness?: number;
|
|
60
|
+
/** Sheen strength at grazing angles. Default 0.06; 0.04 on a male record. */
|
|
61
|
+
sheen?: number;
|
|
62
|
+
/** Sheen colour, linear RGB. Default a warm red; a less pink tone on a male record. */
|
|
63
|
+
sheenColor?: [number, number, number];
|
|
64
|
+
/** Specular intensity. Default 0.6; 0.5 on a male record. */
|
|
65
|
+
specular?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Multiplier on the skin's base colour, linear RGB. Default none; a slight
|
|
68
|
+
* cool one on a male record (the published male albedo runs redder than a
|
|
69
|
+
* Reallusion reference head: R/B 2.0 against 1.87, measured on skin).
|
|
70
|
+
*/
|
|
71
|
+
tint?: [number, number, number];
|
|
72
|
+
}
|
|
73
|
+
export interface SurfaceTuning {
|
|
74
|
+
/** `false` leaves skin materials as shipped. */
|
|
75
|
+
skin?: boolean | SkinTuning;
|
|
76
|
+
/** `false` leaves hair, brow and lash materials as shipped. */
|
|
77
|
+
hair?: boolean;
|
|
78
|
+
/** `false` leaves the scalp under the hair unshaded. */
|
|
79
|
+
scalp?: boolean;
|
|
80
|
+
/** Texture anisotropy. Clamped by the GPU. Default 16. */
|
|
81
|
+
anisotropy?: number;
|
|
82
|
+
/**
|
|
83
|
+
* The avatar record's gender, when the resolver gave one. The published
|
|
84
|
+
* bodies ship lashes with an alpha multiplier of ~3.5 (full lashes, which
|
|
85
|
+
* read as mascara); on a male record it is capped at `LASH_MALE`, and
|
|
86
|
+
* without a record the lashes stay as shipped.
|
|
87
|
+
*/
|
|
88
|
+
gender?: string;
|
|
89
|
+
/** Cap on the lashes' export alpha multiplier; set by `gender`, or directly. */
|
|
90
|
+
lashOpacity?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The lash alpha multiplier a male record is capped to: the texture's own
|
|
94
|
+
* alpha, no multiplier. At 1.6 the lashes were still the darkest line on a
|
|
95
|
+
* male face whose brows and stubble are faint, and read as the feature.
|
|
96
|
+
*/
|
|
97
|
+
export declare const LASH_MALE = 1;
|
|
98
|
+
export interface SurfaceReport {
|
|
99
|
+
skin: number;
|
|
100
|
+
hair: number;
|
|
101
|
+
textures: number;
|
|
102
|
+
/** Head vertices shaded as scalp. */
|
|
103
|
+
scalp: number;
|
|
104
|
+
}
|
|
105
|
+
/** Mean linear colour of a texture's opaque texels, or null if unreadable. */
|
|
106
|
+
export declare function meanTextureColor(tex: THREE.Texture | null | undefined): THREE.Color | null;
|
|
107
|
+
/**
|
|
108
|
+
* Mean linear colour of a texture where a mesh actually uses it: sampled at
|
|
109
|
+
* every third vertex's UV. A head atlas is mostly empty or not skin (mouth,
|
|
110
|
+
* ears, unused space), so its own mean said nothing about the face (luma
|
|
111
|
+
* 0.013 measured against 0.026 sampled at the mesh). Null if unreadable.
|
|
112
|
+
*/
|
|
113
|
+
export declare function meanSkinColor(mesh: THREE.Mesh, tex: THREE.Texture | null | undefined): THREE.Color | null;
|
|
114
|
+
/** three's physical lighting setup, with hair specular faded at the silhouette. */
|
|
115
|
+
export declare function patchHairSpecular(fs: string): string;
|
|
116
|
+
/**
|
|
117
|
+
* Coverage that follows the strand's own alpha. three's WebGL
|
|
118
|
+
* alpha-to-coverage applies the cut as a step (every texel fully in or
|
|
119
|
+
* out), so a fringe read as sparse separate strings with skin between them;
|
|
120
|
+
* coverage proportional to alpha (as WebGPU's is, and as the studio films
|
|
121
|
+
* were rendered) draws faint strand texels faintly and the hair reads full
|
|
122
|
+
* and soft, with alpha scaled back up on the smaller mips. Only
|
|
123
|
+
* near-empty texels are discarded.
|
|
124
|
+
*/
|
|
125
|
+
export declare function patchHairCoverage(fs: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Seamless pore and fine-crease detail as a tangent-space normal map,
|
|
128
|
+
* deterministic (seeded) so every load is identical. Shared by every
|
|
129
|
+
* character: it is never stored on a material, so `dispose()` does not free
|
|
130
|
+
* it out from under another body.
|
|
131
|
+
*/
|
|
132
|
+
export declare function poreDetailTexture(): THREE.DataTexture;
|
|
133
|
+
/** three's shadowmap_vertex, with the normal offset clamped to face scale. */
|
|
134
|
+
export declare function skinShadowVertex(chunk: string): string;
|
|
135
|
+
/**
|
|
136
|
+
* A lit fragment shader receiving shadow as described above. Unchanged when
|
|
137
|
+
* it has no lights (or is already patched).
|
|
138
|
+
*/
|
|
139
|
+
export declare function patchShadowReceiver(fragmentShader: string): string;
|
|
140
|
+
/** three's lights_fragment_begin, with skin's shadow receiver. */
|
|
141
|
+
export declare function skinLightsBegin(chunk: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* Patch three's WebGL physical fragment shader for skin. Exported for the
|
|
144
|
+
* test, which runs it on three's real shader source. A shader without the
|
|
145
|
+
* anchors (a future three, a custom material) is returned unchanged.
|
|
146
|
+
*/
|
|
147
|
+
export declare function patchSkinShader(fragmentShader: string, withDetail: boolean): string;
|
|
148
|
+
/**
|
|
149
|
+
* Darken the head's vertices that sit under hair: within `near` of a hair
|
|
150
|
+
* vertex lying over them (within 60° of the skin's outward normal), above
|
|
151
|
+
* the eyes. Hair beside the skin does not count: at the hairline the fringe
|
|
152
|
+
* lies along the forehead, and counting it tinted a hard brown strip below
|
|
153
|
+
* the hairline (the published heads have ~2.6k vertices, so a tinted row
|
|
154
|
+
* reads as a band). The shade also scales with how much hair is over the
|
|
155
|
+
* vertex, so it fades out through the thinning hairline. Reads skinned positions in WORLD space, so call it
|
|
156
|
+
* after the body is scaled to metres and before anything animates it.
|
|
157
|
+
*/
|
|
158
|
+
export declare function shadeScalp(root: THREE.Object3D, opts?: {
|
|
159
|
+
near?: number;
|
|
160
|
+
far?: number;
|
|
161
|
+
}): number;
|
|
162
|
+
/**
|
|
163
|
+
* Depth in the hair: strands near the scalp sit under the ones above them and
|
|
164
|
+
* get less light, so a real head of hair is darker at the roots and inside
|
|
165
|
+
* than on its outer shell. Card hair carries none of that unless its texture
|
|
166
|
+
* bakes it in (the long cuts do, a short light cut did not and read as a flat
|
|
167
|
+
* cap). Each hair vertex is darkened by how close it lies to the head,
|
|
168
|
+
* against this hair's own thickness (its 90th-percentile distance from the
|
|
169
|
+
* head), as a vertex colour that multiplies the diffuse: roots to ~45% on
|
|
170
|
+
* light hair, less on dark hair, where the texture already carries the depth
|
|
171
|
+
* and the same darkening would have read as a different cut. Reads skinned
|
|
172
|
+
* positions in WORLD space: call it after the body is scaled to metres and
|
|
173
|
+
* before anything animates it. Returns the number of hair vertices shaded.
|
|
174
|
+
*/
|
|
175
|
+
export declare function shadeHairDepth(root: THREE.Object3D): number;
|
|
176
|
+
/**
|
|
177
|
+
* Upgrade skin and hair materials in place (skin materials are replaced on
|
|
178
|
+
* their meshes; the originals are disposed, their textures kept). Run once
|
|
179
|
+
* after load. `shadeScalp` is separate because it needs the body in metres.
|
|
180
|
+
*/
|
|
181
|
+
export declare function applySurfaces(root: THREE.Object3D, tuning?: SurfaceTuning): SurfaceReport;
|