You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 18:18:02