如何用Three JS和React-Three-Fibre基于缓冲数组渲染多个Points?
解决React Three Fiber中渲染多个Points的问题
你的代码存在几个关键问题导致无法渲染多个点,下面是具体修正方案:
1. 缓冲数组初始化错误
Float32Array 仅支持一维数组,直接传入二维数组会导致数据解析异常。同时Three.js的颜色值需要归一化到0-1区间(你当前使用的是0-255的RGB值,不符合要求)。
正确的缓冲数组创建方式:
// 位置:扁平化一维数组,每个点占3个元素(x,y,z) const positions = new Float32Array([0,0,0, 3,3,3, 6,6,6]); // 颜色:归一化到0-1区间,每个点占3个元素(r,g,b) const colors = new Float32Array([92/255,123/255,23/255, 123/255,12/255,233/255, 23/255,98/255,198/255]); // 大小:每个点占1个元素 const sizes = new Float32Array([1,2,3]);
2. 正确传递顶点属性到Points
在React Three Fiber中,需要通过bufferGeometry定义顶点属性,而非直接给Points传props。同时,顶点大小需要额外配置材质才能生效:
import { Points, BufferGeometry, pointsMaterial } from '@react-three/fiber'; // 组件内部代码 return ( {/* 注意:Points无需包裹在mesh里,mesh用于网格模型渲染 */} <Points> <bufferGeometry> <bufferAttribute attach="attributes-position" count={3} // 点的总数量 array={positions} itemSize={3} // 每个点的位置数据长度 /> <bufferAttribute attach="attributes-color" count={3} array={colors} itemSize={3} /> <bufferAttribute attach="attributes-size" count={3} array={sizes} itemSize={1} /> </bufferGeometry> <pointsMaterial vertexColors // 启用顶点颜色 sizeAttenuation // 开启大小衰减(可选,让远点视觉上更小) onBeforeCompile={(shader) => { // 让材质读取自定义的顶点大小属性 shader.vertexShader = ` attribute float size; ${shader.vertexShader} `.replace( 'gl_PointSize = size;', 'gl_PointSize = size;' ); }} /> </Points> );
3. 生成100个随机点的完整实现
如果要生成100个随机位置、颜色、大小的点,可通过循环生成扁平化数组:
import { useState, useMemo } from 'react'; import { Canvas, Points, BufferGeometry, pointsMaterial } from '@react-three/fiber'; const RandomPoints = () => { const pointCount = 100; // 用useMemo缓存缓冲数组,避免重复渲染时重新生成数据 const { positions, colors, sizes } = useMemo(() => { const positionsArr = []; const colorsArr = []; const sizesArr = []; for (let i = 0; i < pointCount; i++) { // 随机位置:范围-10到10 positionsArr.push( Math.random() * 20 - 10, Math.random() * 20 - 10, Math.random() * 20 - 10 ); // 随机颜色:归一化到0-1区间 colorsArr.push( Math.random(), Math.random(), Math.random() ); // 随机大小:范围1到5 sizesArr.push(Math.random() * 4 + 1); } return { positions: new Float32Array(positionsArr), colors: new Float32Array(colorsArr), sizes: new Float32Array(sizesArr) }; }, [pointCount]); return ( <Points> <bufferGeometry> <bufferAttribute attach="attributes-position" count={pointCount} array={positions} itemSize={3} /> <bufferAttribute attach="attributes-color" count={pointCount} array={colors} itemSize={3} /> <bufferAttribute attach="attributes-size" count={pointCount} array={sizes} itemSize={1} /> </bufferGeometry> <pointsMaterial vertexColors sizeAttenuation onBeforeCompile={(shader) => { shader.vertexShader = ` attribute float size; ${shader.vertexShader} `.replace( 'gl_PointSize = size;', 'gl_PointSize = size;' ); }} /> </Points> ); }; export default function App() { return ( <Canvas camera={{ position: [0, 0, 30] }}> <ambientLight /> <RandomPoints /> </Canvas> ); }
额外注意事项
Points是独立的渲染对象,不需要包裹在<mesh>组件内。- 使用
useMemo缓存缓冲数组可提升性能,避免组件每次渲染都重新生成大量数据。 - 如果不需要每个点大小不同,可去掉
size相关属性,直接设置pointsMaterial的size固定值。
内容的提问来源于stack exchange,提问作者stopbanningmelmao123
相关产品推荐
相关产品推荐

