Three.js中为BufferGeometry创建的点添加速度、加速度并实现渲染循环更新
BufferGeometry 下点对象动态属性的适配改造方案
核心差异说明
旧版Geometry的顶点是独立Vector3实例,可以直接挂载速度、加速度这类自定义字段;BufferGeometry的顶点数据统一存储在连续类型化数组中,不支持在单个顶点对象上随意挂载自定义属性,需要通过和顶点索引一一对应的并行数组存储自定义物理量,配合内置的位置数组完成动态更新。
改造实现步骤
- 初始化阶段:创建固定长度的位置、速度、加速度类型化数组,一次性填充随机初始值,避免逐帧生成临时对象
- 渲染循环阶段:直接操作位置数组的连续内存,按照原有物理逻辑逐点更新速度、z坐标值
- 更新完成后:将位置顶点属性的
needsUpdate标记置为true,通知Three.js将最新数据提交到GPU完成重绘
改造后完整实现代码
初始化部分(替换原有初始化逻辑)
const POINT_COUNT = 10; const img = new THREE.TextureLoader().load('image'); const pointMaterial = new THREE.PointsMaterial({ map: img, transparent: true }); const bufferGeometry = new THREE.BufferGeometry(); // 顶点位置连续数组,每个点占x/y/z3个连续存储位 const positionArr = new Float32Array(POINT_COUNT * 3); // 自定义物理量并行数组,索引和顶点一一对应 const velocityArr = new Float32Array(POINT_COUNT); const accelerationArr = new Float32Array(POINT_COUNT); for (let i = 0; i < POINT_COUNT; i++) { // 填充初始顶点坐标 positionArr[i * 3] = Math.random() * 20 - 10; positionArr[i * 3 + 1] = Math.random() * 20 - 10; positionArr[i * 3 + 2] = Math.random() * 20 - 10; // 初始化随机速度、加速度 velocityArr[i] = Math.random() * 0.1; accelerationArr[i] = Math.random() * 0.02; } // 绑定位置顶点属性 bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positionArr, 3)); bufferGeometry.computeVertexNormals(); const pointsMesh = new THREE.Points(bufferGeometry, pointMaterial); scene.add(pointsMesh);
渲染循环部分(替换原有适配Geometry的更新逻辑)
function render() { const positionAttr = bufferGeometry.attributes.position; const positionData = positionAttr.array; for (let i = 0; i < POINT_COUNT; i++) { // 完全复用原有业务逻辑:速度累加加速度 velocityArr[i] += accelerationArr[i]; // 更新对应点的z坐标 positionData[i * 3 + 2] -= velocityArr[i]; } // 标记顶点数据为已更新,触发GPU端数据同步 positionAttr.needsUpdate = true; // 渲染器逐帧绘制 renderer.render(scene, camera); requestAnimationFrame(render); }
常见实现注意事项
- 不要逐帧将连续数组转成
Vector3对象遍历,每帧生成大量临时对象会造成频繁GC,拖垮运行性能 - 旧版
Geometry的verticesNeedUpdate全局标记,在BufferGeometry下对应单个顶点属性的needsUpdate标记,只需要对被修改的position属性设置该标记即可,不要遗漏设置,否则即使CPU端修改了数组值,GPU端仍会读取旧的顶点缓存,看不到动态效果 - 速度、加速度这类仅在CPU端计算的属性,不需要单独创建BufferAttribute提交到显存,会带来不必要的显存和数据传输开销
内容的提问来源于stack exchange,提问作者create_deploy
相关产品推荐
相关产品推荐

