Three.js BufferGeometry实现三角形仅扩散不放大的动画方案
Three.js 三角形保持尺寸不变的扩散效果实现方案
你之前直接缩放mesh导致三角形变大的原因是:
mesh.scale会对所有顶点的全局坐标做整体乘法,会同时放大三角形自身的顶点间距,所以尺寸会改变。要保持三角形大小不变,只需要单独缩放每个三角形的中心点坐标,保留每个三角形三个顶点相对于自身中心点的固定偏移量即可。
第一步:预存原始锚点与顶点偏移
首先在初始化阶段新增两个数组,存储每个三角形的原始中心点(锚点)和每个顶点相对于锚点的固定偏移:
// 原有变量之外新增 const triangleCenters = []; // 存储每个三角形的原始中心点 const vertexOffsets = []; // 存储每个顶点相对于对应三角形中心的偏移量 geometry = new THREE.BufferGeometry(); geometry.dynamic = true; const triangles = 1000; // 保留你原来定义的三角形数量即可 positions = new Float32Array(triangles * 3 * 3); const normals = new Float32Array(triangles * 3 * 3); const colors = new Float32Array(triangles * 3 * 3); const color = new THREE.Color(); const n = 200, n2 = n / 2; // 三角形最大扩散范围 const d = 1000, d2 = d / 2; // 单个三角形固定尺寸 const pA = new THREE.Vector3(); const pB = new THREE.Vector3(); const pC = new THREE.Vector3(); const cb = new THREE.Vector3(); const ab = new THREE.Vector3(); for (let i = 0; i < positions.length; i += 9) { // 生成三角形原始中心点(锚点) const centerX = Math.random() * n - n2; const centerY = Math.random() * n - n2; const centerZ = Math.random() * n - n2; triangleCenters.push({x: centerX, y: centerY, z: centerZ}); // 生成三个顶点相对于锚点的固定偏移 const axOffset = Math.random() * d - d2; const ayOffset = Math.random() * d - d2; const azOffset = Math.random() * d - d2; const bxOffset = Math.random() * d - d2; const byOffset = Math.random() * d - d2; const bzOffset = Math.random() * d - d2; const cxOffset = Math.random() * d - d2; const cyOffset = Math.random() * d - d2; const czOffset = Math.random() * d - d2; vertexOffsets.push( axOffset, ayOffset, azOffset, bxOffset, byOffset, bzOffset, cxOffset, cyOffset, czOffset ); // 初始状态所有三角形聚集在场景中心(锚点缩放系数为0) positions[i] = 0 + axOffset; positions[i + 1] = 0 + ayOffset; positions[i + 2] = 0 + azOffset; positions[i + 3] = 0 + bxOffset; positions[i + 4] = 0 + byOffset; positions[i + 5] = 0 + bzOffset; positions[i + 6] = 0 + cxOffset; positions[i + 7] = 0 + cyOffset; positions[i + 8] = 0 + czOffset; // 后面法线、颜色生成逻辑和你原有代码完全一致,直接保留即可 // ... 省略原有法线、颜色计算代码 } geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3)); geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3)); geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); geometry.computeBoundingSphere(); let material = new THREE.MeshPhongMaterial({ color: 0xaaaaaa, specular: 0xffffff, shininess: 250, side: THREE.DoubleSide, vertexColors: true, }); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // 新增扩散系数变量,初始为0 let spreadFactor = 0;
第二步:编写动画循环更新逻辑
在你的requestAnimationFrame动画循环中,更新扩散系数后重算所有顶点的位置:
function animate() { requestAnimationFrame(animate); // 扩散系数缓慢增加,调整增速可控制扩散速度 if (spreadFactor < 1) { // 去掉判断可实现无限扩散 spreadFactor += 0.005; } const positions = mesh.geometry.attributes.position.array; // 遍历所有三角形更新位置 for (let i = 0; i < triangleCenters.length; i++) { const center = triangleCenters[i]; // 计算当前三角形扩散后的中心点 const currentCenterX = center.x * spreadFactor; const currentCenterY = center.y * spreadFactor; const currentCenterZ = center.z * spreadFactor; // 每个顶点位置 = 扩散后中心点 + 固定偏移 const posIndex = i * 9; positions[posIndex] = currentCenterX + vertexOffsets[posIndex]; positions[posIndex + 1] = currentCenterY + vertexOffsets[posIndex + 1]; positions[posIndex + 2] = currentCenterZ + vertexOffsets[posIndex + 2]; positions[posIndex + 3] = currentCenterX + vertexOffsets[posIndex + 3]; positions[posIndex + 4] = currentCenterY + vertexOffsets[posIndex + 4]; positions[posIndex + 5] = currentCenterZ + vertexOffsets[posIndex + 5]; positions[posIndex + 6] = currentCenterX + vertexOffsets[posIndex + 6]; positions[posIndex + 7] = currentCenterY + vertexOffsets[posIndex + 7]; positions[posIndex + 8] = currentCenterZ + vertexOffsets[posIndex + 8]; } // 标记位置属性需要更新,Three.js才会重渲染 mesh.geometry.attributes.position.needsUpdate = true; // 保留你原有的渲染逻辑 renderer.render(scene, camera); } animate();
性能优化提示
如果三角形数量超过10万,可改用THREE.InstancedMesh实现,每个三角形作为独立实例,直接更新实例矩阵即可,不需要每次遍历修改整个position数组,渲染性能会有明显提升。
内容的提问来源于stack exchange,提问作者Cole Ogrodnick
相关产品推荐
相关产品推荐

