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

THREE.ShaderMaterial是否为每个粒子创建着色器?粒子间数据共享咨询

解决粒子引力模拟的着色器数据共享问题

首先纠正你的误解:Three.js的ShaderMaterial并不是给每个粒子创建独立着色器,所有粒子共用同一个编译好的着色器程序。你传入的uPositions uniform是全局共享的,所有粒子的顶点着色器都能访问到同一个数组数据。

接下来分两种场景解决粒子位置更新和共享的问题:

一、CPU端更新(适合粒子数量较少的场景)

如果粒子数量不多(比如几千个以内),直接在CPU里计算每个粒子的新位置,然后更新uniform数组即可:

  1. 在每一帧的渲染循环中,遍历positionsVec3数组,根据引力公式计算每个粒子受其他粒子的引力,更新其位置值。
  2. 更新完成后,设置material.uniforms.uPositions.needsUpdate = true,告诉Three.js把新的位置数据上传到GPU。

示例代码片段:

function animate() {
  requestAnimationFrame(animate);

  // 计算所有粒子的新位置
  for (let i = 0; i < positionsVec3.length; i += 3) {
    let x = positionsVec3[i], y = positionsVec3[i+1], z = positionsVec3[i+2];
    // 计算其他粒子对当前粒子的引力,更新x/y/z
    // ... 引力计算逻辑 ...
    positionsVec3[i] = x;
    positionsVec3[i+1] = y;
    positionsVec3[i+2] = z;
  }

  // 标记uniform需要更新
  material.uniforms.uPositions.needsUpdate = true;

  renderer.render(scene, camera);
}

二、GPU端更新(适合大量粒子的高性能场景)

如果粒子数量上万甚至更多,CPU计算会成为瓶颈,这时候用WebGL 2.0的TransformFeedback特性,让GPU直接计算并更新粒子位置,不需要回传CPU:

  1. 创建两个Float32Array缓冲,分别存储当前帧和下一帧的粒子位置。
  2. 配置ShaderMaterial的transformFeedback属性,指定要捕获的输出变量(比如新位置)。
  3. 在顶点着色器中,读取当前所有粒子的位置,计算每个粒子的新位置并输出。
  4. 每一帧渲染时,用TransformFeedback把计算结果写入另一个缓冲,然后交换两个缓冲作为下一帧的输入。

示例配置片段:

// 创建两个位置缓冲
const currentPositions = new Float32Array(positionsVec3);
const nextPositions = new Float32Array(positionsVec3.length);

// 配置TransformFeedback
const transformFeedback = new THREE.TransformFeedback(renderer, {
  uniforms: material.uniforms,
  vertexShader: galaxyVortexShader,
  fragmentShader: galaxyFragmentShader,
  // 指定要捕获的输出变量名
  varyings: ['vNewPosition']
});

// 在顶点着色器中计算新位置并输出
// 顶点着色器示例:
// attribute vec3 aPosition;
// uniform vec3 uPositions[];
// varying vec3 vNewPosition;
// void main() {
//   // 计算当前粒子受其他粒子的引力,得到新位置vNewPosition
//   // ... 引力计算逻辑 ...
//   vNewPosition = newPosition;
// }

function animate() {
  requestAnimationFrame(animate);

  // 使用TransformFeedback计算新位置
  transformFeedback.render(scene, camera, {
    // 把结果写入nextPositions缓冲
    outputBuffer: nextPositions
  });

  // 交换缓冲,下一帧用nextPositions作为输入
  [currentPositions, nextPositions] = [nextPositions, currentPositions];
  material.uniforms.uPositions.value = currentPositions;
  material.uniforms.uPositions.needsUpdate = true;

  renderer.render(scene, camera);
}

注意:使用TransformFeedback需要WebGL 2.0支持,Three.js会自动检测环境是否兼容。

内容的提问来源于stack exchange,提问作者Bastien

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 14:54:15