如何用ThreeJS高效绘制百万个纯色填充矩形?
问题:ThreeJS高效渲染百万纯色填充矩形
需求:使用ThreeJS绘制百万个纯色填充矩形,已获取所有矩形的坐标。要求内存占用高效,通过单次绘制调用完成所有图形渲染,避免浪费GPU资源。尝试多种方法均未成功,附上测试代码与坐标,请求帮助。
尝试的代码
const geometry = new BufferGeometry(); // 用Float32Array存储顶点位置 const positions = new Float32Array(4 * 3 * this.customShapes.length); const indexArray = []; let indexCounter = 0; for (let i = 0; i < this.customShapes.length; i++) { let rectShape = this.customShapes[i]; positions[i * 12 + 0] = rectShape.x1; positions[i * 12 + 1] = rectShape.y1; positions[i * 12 + 2] = 0; positions[i * 12 + 3] = rectShape.x1; positions[i * 12 + 4] = rectShape.y2; positions[i * 12 + 5] = 0; positions[i * 12 + 6] = rectShape.x2; positions[i * 12 + 7] = rectShape.y2; positions[i * 12 + 8] = 0; positions[i * 12 + 9] = rectShape.x2; positions[i * 12 + 10] = rectShape.y1; positions[i * 12 + 11] = 0; indexArray[i * 6 + 0] = indexCounter; indexArray[i * 6 + 1] = indexCounter + 1; indexArray[i * 6 + 2] = indexCounter + 2; indexArray[i * 6 + 3] = indexCounter; indexArray[i * 6 + 4] = indexCounter + 2; indexArray[i * 6 + 5] = indexCounter + 3; indexCounter += 4; } geometry.setAttribute('position', new BufferAttribute(positions, 3)); geometry.setIndex(indexArray); const material = new MeshBasicMaterial({ color: color }); // 创建网格并添加到场景 const mesh = new Mesh(geometry, material); mesh.name = this.key; group.add(mesh);
测试坐标(其中一个矩形无法渲染)
[ { "Coordinates": [ [ -0.45, 0.89 ], [ -0.45, 1.0 ], [ 0.48, 1.0 ], [ 0.48, 0.89 ] ] }, { "Coordinates": [ [ 0.6699999999999999, 2.69 ], [ 0.6699999999999999, 0.89 ], [ 0.81, 0.89 ], [ 0.81, 2.69 ] ] } ]
问题分析与解决方案
1. 单个矩形不显示的直接原因
测试坐标中第二个矩形的顶点顺序是顺时针,而ThreeJS默认将逆时针顶点顺序定义为正面,MeshBasicMaterial默认只渲染正面(side: FrontSide),导致该矩形被当作背面剔除,无法显示。
解决方法二选一:
- 调整第二个矩形的顶点顺序为逆时针;
- 修改材质配置,开启双面渲染:
const material = new MeshBasicMaterial({ color: color, side: DoubleSide });
2. 百万级矩形的内存与性能优化方案
你的当前方案每个矩形占用4个顶点+6个索引,百万级数据会产生400万顶点、600万索引,内存压力大。推荐使用InstancedBufferGeometry,通过实例化渲染复用单个矩形的几何体,内存占用骤降:
// 创建基础矩形几何体(仅需一次定义) const baseGeometry = new BufferGeometry(); const basePositions = new Float32Array([ -0.5, 0.5, 0, // 左上角 -0.5, -0.5, 0, // 左下角 0.5, -0.5, 0, // 右下角 0.5, 0.5, 0 // 右上角 ]); const baseIndices = new Uint16Array([0, 1, 2, 0, 2, 3]); // 两个三角形组成矩形 baseGeometry.setAttribute('position', new BufferAttribute(basePositions, 3)); baseGeometry.setIndex(new BufferAttribute(baseIndices, 1)); // 准备实例化数据:每个矩形的变换矩阵(平移+缩放) const instanceCount = this.customShapes.length; const matrices = new Float32Array(instanceCount * 16); // 每个矩阵含16个浮点值 const matrix = new Matrix4(); for (let i = 0; i < instanceCount; i++) { const rect = this.customShapes[i]; const width = rect.x2 - rect.x1; const height = rect.y2 - rect.y1; // 先平移到矩形中心,再按宽高缩放 matrix.makeTranslation((rect.x1 + rect.x2)/2, (rect.y1 + rect.y2)/2, 0) .multiply(new Matrix4().makeScale(width, height, 1)); // 将矩阵写入数组 matrix.toArray(matrices, i * 16); } // 创建实例化几何体 const instancedGeometry = new InstancedBufferGeometry().copy(baseGeometry); instancedGeometry.setAttribute('instanceMatrix', new InstancedBufferAttribute(matrices, 16)); // 创建材质并渲染 const material = new MeshBasicMaterial({ color: color, side: DoubleSide }); const mesh = new Mesh(instancedGeometry, material); group.add(mesh);
这种方式仅用4个顶点、6个索引就能渲染任意数量的矩形,内存占用仅为原方案的几十万分之一,且单次绘制调用完成,完全适配百万级需求。
3. 原代码的其他优化点
- 索引数组改用TypedArray:百万级数据下,
indexArray应使用Uint32Array(顶点数≤65535时用Uint16Array),避免普通数组的额外内存开销:const indexArray = new Uint32Array(6 * this.customShapes.length); - 确认坐标映射正确:确保代码中
rectShape.x1/y1/x2/y2与测试数据的Coordinates数组对应,比如x1对应Coordinates[0][0],y1对应Coordinates[0][1],避免属性取值错误。
内容的提问来源于stack exchange,提问作者Vikram Jain
相关产品推荐
相关产品推荐

