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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 06:35:11