Three.js:通过鼠标点击与移动创建/更新Mesh
Got it, let's tackle moving your click-to-add-points + mouse-follow functionality from a Line to a Mesh in Three.js. Here's how to approach it step by step:
First, a key difference to note: unlike Lines that only need a sequence of points, Meshes rely on faces (triangles/quads) to render. So we can't reuse your Line's point array logic directly—we'll need to handle face indices alongside vertex positions.
1. Initialize the Core Structure
We'll need to track three key pieces of data: fixed vertices added via clicks, a temporary vertex that follows the mouse, and face indices to define the Mesh's geometry.
// Track fixed vertices and the mouse-following temp vertex const fixedPoints = []; const tempPoint = new THREE.Vector3(); // Create dynamic BufferGeometry const geometry = new THREE.BufferGeometry(); // Pre-allocate space for positions (avoids frequent resizing) const positions = new Float32Array(1000 * 3); // Supports up to 1000 points geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // Pre-allocate space for face indices (each triangle uses 3 indices) const indices = new Uint32Array(1000 * 3); geometry.setIndex(new THREE.BufferAttribute(indices, 1)); // Create a debug-friendly material (adjust to your needs) const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide, // Render both sides of faces wireframe: true // Optional: helps visualize vertices/faces during testing }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh);
2. Handle Mouse Clicks: Add Fixed Vertices & Build Faces
Each click will lock the current temp vertex into the fixed points array, then generate a new triangular face using the latest three points.
function onMouseClick(event) { // Save the temp vertex to fixed points fixedPoints.push(tempPoint.clone()); const totalFixedPoints = fixedPoints.length; // Update positions array with the new fixed point const positionsArray = geometry.attributes.position.array; tempPoint.toArray(positionsArray, (totalFixedPoints - 1) * 3); // Add a new triangular face if we have at least 3 fixed points if (totalFixedPoints >= 3) { const indicesArray = geometry.index.array; // Index for the new face's start const faceStartIndex = (totalFixedPoints - 3) * 3; // Assign indices for the triangle: [prev-prev, prev, new point] indicesArray[faceStartIndex] = totalFixedPoints - 3; indicesArray[faceStartIndex + 1] = totalFixedPoints - 2; indicesArray[faceStartIndex + 2] = totalFixedPoints - 1; // Update draw range for indices to include the new face geometry.index.setDrawRange(0, (totalFixedPoints - 2) * 3); } // Update draw range for positions and mark attributes as needing updates geometry.attributes.position.setDrawRange(0, totalFixedPoints); geometry.attributes.position.needsUpdate = true; geometry.index.needsUpdate = true; } window.addEventListener('click', onMouseClick);
3. Handle Mouse Movement: Update the Temporary Vertex
This part mirrors your Line logic, but we'll also adjust the latest face's index to follow the temp vertex as it moves.
function onMouseMove(event) { // Convert mouse position to normalized device coordinates (-1 to 1) const mouse = new THREE.Vector2(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // Raycast to get 3D position on a target plane (adjust plane to your scene) const raycaster = new THREE.Raycaster(); raycaster.setFromCamera(mouse, camera); const targetPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // XY plane at z=0 const intersection = new THREE.Vector3(); raycaster.ray.intersectPlane(targetPlane, intersection); // Update the temp vertex with the intersection point tempPoint.copy(intersection); const totalFixedPoints = fixedPoints.length; const positionsArray = geometry.attributes.position.array; if (totalFixedPoints === 0) { // No fixed points yet: just update the first position slot geometry.attributes.position.setDrawRange(0, 1); tempPoint.toArray(positionsArray, 0); } else { // Update the temp vertex slot (right after fixed points) tempPoint.toArray(positionsArray, totalFixedPoints * 3); geometry.attributes.position.setDrawRange(0, totalFixedPoints + 1); // If we have 2+ fixed points, update the latest face's third index to track the temp point if (totalFixedPoints >= 2) { const indicesArray = geometry.index.array; const lastFaceThirdIndex = (totalFixedPoints - 2) * 3 + 2; indicesArray[lastFaceThirdIndex] = totalFixedPoints; geometry.index.needsUpdate = true; } } geometry.attributes.position.needsUpdate = true; } window.addEventListener('mousemove', onMouseMove);
4. Key Notes for Smooth Implementation
- Face Logic Flexibility: The example uses triangular faces that build outward with each new point. If you need quad faces or a different shape, adjust how you generate indices.
- Performance: Pre-allocating large arrays (like 1000 points) avoids the overhead of resizing buffers repeatedly.
- Material Tweaks: If you switch from wireframe to a solid material, run
geometry.computeVertexNormals()to ensure proper lighting. - Initial State: Meshes can't render with fewer than 3 vertices. You might want to hide the mesh until the third click, or pair it with a
Pointsobject to show the temp vertex early on.
内容的提问来源于stack exchange,提问作者spfuller

