|
35 | 35 |
|
36 | 36 | import * as THREE from 'three'; |
37 | 37 |
|
38 | | - import Stats from 'three/addons/libs/stats.module.js'; |
39 | | - |
40 | 38 | import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; |
41 | 39 | import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; |
42 | 40 |
|
|
108 | 106 | // '': 'tongueOut' |
109 | 107 | }; |
110 | 108 |
|
111 | | - const video = document.createElement( 'video' ); |
112 | | - |
113 | | - const filesetResolver = await FilesetResolver.forVisionTasks( |
114 | | - 'https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/wasm' |
115 | | - ); |
116 | | - |
117 | | - const faceLandmarker = await FaceLandmarker.createFromOptions( filesetResolver, { |
118 | | - baseOptions: { |
119 | | - modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task', |
120 | | - delegate: 'GPU' |
121 | | - }, |
122 | | - outputFaceBlendshapes: true, |
123 | | - outputFacialTransformationMatrixes: true, |
124 | | - runningMode: 'VIDEO', |
125 | | - numFaces: 1 |
126 | | - } ); |
127 | | - |
128 | | - if ( navigator.mediaDevices && navigator.mediaDevices.getUserMedia ) { |
129 | | - |
130 | | - navigator.mediaDevices.getUserMedia( { video: { facingMode: 'user' } } ) |
131 | | - .then( function ( stream ) { |
132 | | - |
133 | | - video.srcObject = stream; |
134 | | - video.play(); |
135 | | - |
136 | | - } ) |
137 | | - .catch( function ( error ) { |
138 | | - |
139 | | - console.error( 'Unable to access the camera/webcam.', error ); |
140 | | - |
141 | | - } ); |
142 | | - |
143 | | - } |
144 | | - |
145 | 109 | // |
146 | 110 |
|
147 | 111 | const renderer = new THREE.WebGLRenderer( { antialias: true } ); |
|
178 | 142 | const mesh = gltf.scene.children[ 0 ]; |
179 | 143 | scene.add( mesh ); |
180 | 144 |
|
| 145 | + const head = mesh.getObjectByName( 'mesh_2' ); |
| 146 | + head.material = new THREE.MeshNormalMaterial(); |
| 147 | + |
181 | 148 | // GUI |
182 | 149 |
|
183 | 150 | const gui = new GUI(); |
184 | 151 | gui.close(); |
185 | 152 |
|
186 | | - const head = mesh.getObjectByName( 'mesh_2' ); |
187 | | - head.material = new THREE.MeshNormalMaterial(); |
188 | 153 | const influences = head.morphTargetInfluences; |
189 | 154 |
|
190 | 155 | for ( const [ key, value ] of Object.entries( head.morphTargetDictionary ) ) { |
|
194 | 159 | .listen( influences ); |
195 | 160 |
|
196 | 161 | } |
| 162 | + |
| 163 | + renderer.setAnimationLoop( animation ); |
197 | 164 |
|
198 | 165 | } ); |
199 | 166 |
|
200 | 167 | // Video Texture |
201 | 168 |
|
| 169 | + const video = document.createElement( 'video' ); |
| 170 | + |
202 | 171 | const texture = new THREE.VideoTexture( video ); |
203 | 172 | texture.colorSpace = THREE.SRGBColorSpace; |
204 | 173 |
|
|
207 | 176 | const videomesh = new THREE.Mesh( geometry, material ); |
208 | 177 | scene.add( videomesh ); |
209 | 178 |
|
210 | | - // |
| 179 | + // MediaPipe |
| 180 | + |
| 181 | + const filesetResolver = await FilesetResolver.forVisionTasks( |
| 182 | + 'https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/wasm' |
| 183 | + ); |
| 184 | + |
| 185 | + const faceLandmarker = await FaceLandmarker.createFromOptions( filesetResolver, { |
| 186 | + baseOptions: { |
| 187 | + modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task', |
| 188 | + delegate: 'GPU' |
| 189 | + }, |
| 190 | + outputFaceBlendshapes: true, |
| 191 | + outputFacialTransformationMatrixes: true, |
| 192 | + runningMode: 'VIDEO', |
| 193 | + numFaces: 1 |
| 194 | + } ); |
211 | 195 |
|
212 | | - const stats = new Stats(); |
213 | | - document.body.appendChild( stats.dom ); |
| 196 | + if ( navigator.mediaDevices && navigator.mediaDevices.getUserMedia ) { |
| 197 | + |
| 198 | + navigator.mediaDevices.getUserMedia( { video: { facingMode: 'user' } } ) |
| 199 | + .then( function ( stream ) { |
| 200 | + |
| 201 | + video.srcObject = stream; |
| 202 | + video.play(); |
| 203 | + |
| 204 | + } ) |
| 205 | + .catch( function ( error ) { |
| 206 | + |
| 207 | + console.error( 'Unable to access the camera/webcam.', error ); |
| 208 | + |
| 209 | + } ); |
| 210 | + |
| 211 | + } |
214 | 212 |
|
215 | 213 | const transform = new THREE.Object3D(); |
216 | 214 |
|
217 | | - renderer.setAnimationLoop( function () { |
| 215 | + function animation() { |
218 | 216 |
|
219 | 217 | if ( video.readyState >= HTMLMediaElement.HAVE_METADATA ) { |
220 | 218 |
|
|
241 | 239 |
|
242 | 240 | if ( results.faceBlendshapes.length > 0 ) { |
243 | 241 |
|
244 | | - const faceBlendshapes = results.faceBlendshapes[ 0 ].categories;; |
| 242 | + const face = scene.getObjectByName( 'mesh_2' ); |
245 | 243 |
|
246 | | - const object = scene.getObjectByName( 'mesh_2' ); |
| 244 | + const faceBlendshapes = results.faceBlendshapes[ 0 ].categories; |
247 | 245 |
|
248 | 246 | for ( const blendshape of faceBlendshapes ) { |
249 | 247 |
|
250 | | - const name = blendshapesMap[ blendshape.categoryName ]; |
251 | | - const index = object.morphTargetDictionary[ name ]; |
| 248 | + const categoryName = blendshape.categoryName; |
| 249 | + const score = blendshape.score; |
| 250 | + |
| 251 | + const index = face.morphTargetDictionary[ blendshapesMap[ categoryName ] ]; |
252 | 252 |
|
253 | 253 | if ( index !== undefined ) { |
254 | 254 |
|
255 | | - object.morphTargetInfluences[ index ] = blendshape.score; |
| 255 | + face.morphTargetInfluences[ index ] = score; |
256 | 256 |
|
257 | 257 | } |
258 | 258 |
|
|
269 | 269 |
|
270 | 270 | controls.update(); |
271 | 271 |
|
272 | | - stats.update(); |
273 | | - |
274 | | - } ); |
| 272 | + } |
275 | 273 |
|
276 | 274 | window.addEventListener( 'resize', function () { |
277 | 275 |
|
|
0 commit comments