Three.js中如何让THREE.Points同步变形目标动画?
Great question—this is a super common gotcha with morph targets in Three.js, since they apply deformations directly on the GPU without modifying the original geometry's CPU-side data. Let's break down your options clearly:
1. Alternative Solutions (Beyond CPU Calculation)
The best alternatives all leverage GPU-side morphing to keep performance high, avoiding costly CPU vertex updates:
- Custom ShaderMaterial for Points: Since
THREE.PointsMaterialdoesn't natively support morph targets, you can extend the built-in points shader to include morph target logic. This lets your points deform on the GPU in sync with the cube, no CPU overhead. - Reuse the Mesh's Morph Target Data: The loaded cube's geometry already contains all the shape key vertex offsets—you just need to make your points material use that data.
2. Efficiently Sync Points with Cube Morph Animation
The optimal approach is to make your THREE.Points use GPU-based morph targets, mirroring how the cube mesh works. Here's a step-by-step implementation:
Step 1: Ensure Geometry Includes Morph Targets
When you load your Blender model (exported with shape keys enabled), the resulting BufferGeometry will have a morphTargets array containing the shape key vertex data.
Step 2: Create a Morph-Aware Points Shader
We'll extend Three.js's built-in points shader to handle morph targets. This way, we reuse the existing points rendering logic while adding deformation support:
// Assume you've loaded your cube mesh as `cubeMesh` const geometry = cubeMesh.geometry; // Clone the built-in points shader uniforms and add morph influences const pointShader = THREE.ShaderLib.points; const uniforms = THREE.UniformsUtils.clone(pointShader.uniforms); uniforms.morphTargetInfluences = { value: cubeMesh.morphTargetInfluences }; // Reuse the cube's influence array // Modify the vertex shader to apply morph target deformations const vertexShader = ` uniform float morphTargetInfluences[ NUM_MORPH_TARGETS ]; ${pointShader.vertexShader.replace( '#include <begin_vertex>', ` #include <begin_vertex> // Apply morph target deformations #ifdef USE_MORPHTARGETS vec3 morphed = vec3(0.0); float totalWeight = 0.0; for (int i = 0; i < NUM_MORPH_TARGETS; i++) { float influence = morphTargetInfluences[i]; if (influence > 0.0) { morphed += (morphTargets[i].attributes.position.xyz - position) * influence; totalWeight += influence; } } transformed += morphed * smoothstep(0.0, 1.0, totalWeight); #endif ` )} `; // Create the shader material with morph target defines const pointMaterial = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vertexShader, fragmentShader: pointShader.fragmentShader, transparent: true, defines: { USE_MORPHTARGETS: '', NUM_MORPH_TARGETS: geometry.morphTargets.length.toString() } }); // Create your points with the modified material const points = new THREE.Points(geometry, pointMaterial); scene.add(points);
Step 3: Sync the Animation
Since we're reusing cubeMesh.morphTargetInfluences directly in the points material's uniforms, any changes you make to the cube's influence values will automatically propagate to the points:
// Example animation loop function animate() { requestAnimationFrame(animate); // Update the cube's morph influence (same affects points) cubeMesh.morphTargetInfluences[0] = (Math.sin(Date.now() * 0.001) + 1) / 2; renderer.render(scene, camera); } animate();
What If You Need CPU-Side Deformed Geometry?
If you absolutely need the CPU-side deformed vertex data (e.g., for collision detection), you can compute it efficiently in batches using BufferAttribute methods, but note this will still have performance costs for large geometries:
const originalPos = geometry.attributes.position.array; const morphPos = geometry.morphTargets[0].attributes.position.array; const influence = cubeMesh.morphTargetInfluences[0]; // Update the points' position attribute directly const updatedPos = points.geometry.attributes.position.array; for (let i = 0; i < originalPos.length; i += 3) { updatedPos[i] = originalPos[i] + (morphPos[i] - originalPos[i]) * influence; updatedPos[i+1] = originalPos[i+1] + (morphPos[i+1] - originalPos[i+1]) * influence; updatedPos[i+2] = originalPos[i+2] + (morphPos[i+2] - originalPos[i+2]) * influence; } points.geometry.attributes.position.needsUpdate = true;
This is only recommended if GPU-side morphing isn't feasible for your use case.
内容的提问来源于stack exchange,提问作者Ravi Gidwani

