Three.js:如何将多个索引和向量数组合并为一个

Three.js: how to combine several indices & vector arrays to one

我正在尝试可视化一个大战略(EU4、CK3、HOI),例如 Three.js 中的地图。我开始为每个细胞创建网格。结果很好(屏幕截图 1 和 2)。

分离网格方法 - 简单的陆地/水域区分:

分离网格方法 - 随机单元格颜色:

但是,如果有很多单元,性能就会成为一个问题(我使用 10k 个单元获得 15fps)。 为了提高性能,我想将所有这些单独的索引和顶点数组组合成 2 个大数组,然后将用于创建单个网格。

我循环遍历所有单元格,将它们的索引、顶点和颜色推入大数组,如下所示:

 addCellGeometryToMapGeometry(cell) {

    let startIndex = this.mapVertices.length;
    let cellIndices = cell.indices.length;
    let cellVertices = cell.vertices.length;

    let color = new THREE.Color( Math.random(), Math.random(), Math.random() );

    for (let i = 0; i < cellIndices; i++) {
        this.mapIndices.push(startIndex + cell.indices[i]);
    }
    for (let i = 0; i < cellVertices; i++) {
        this.mapVertices.push(cell.vertices[i]);
        this.mapColors.push (color);
    }

}

然后我生成组合网格:

 generateMapMesh() {
    let geometry = new THREE.BufferGeometry();
    const material = new THREE.MeshPhongMaterial( {
        side: THREE.DoubleSide,
        flatShading: true,
        vertexColors: true,
        shininess: 0
    } );

    geometry.setIndex( this.mapIndices );
    geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( this.mapVertices, 3 ) );
    geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( new Float32Array(this.mapColors.length), 3 ) );

    for ( let i = 0; i < this.mapColors.length; i ++ ) {
        geometry.attributes.color.setXYZ(i,  this.mapColors[i].r,  this.mapColors[i].g,  this.mapColors[i].b);
    }

    return new THREE.Mesh( geometry, material );
}

不幸的是,结果平淡无奇: 虽然组合数组中的数据看起来没问题,但只有每三个单元格被渲染一次。在某些情况下,索引似乎也会混淆。

组合方法 - 随机单元格颜色:

在其他类似主题中,建议合并现有网格。但是,我认为我的方法应该能让我更好地理解实际发生的事情,并且还可能节省性能。

我的代码是否存在我看不到的明显缺陷?
或者我一般走错路了,如果是这样,应该怎么做呢?

我实际上在我的代码中发现了问题。错误:

let startIndex = this.mapVertices.length;

这里的问题是索引数组中的值总是引用一个顶点(它由顶点数组中的 3 个连续数组条目组成)。正确:

let startIndex = this.mapVertices.length / 3;

此外,我应该只为每个顶点推送一种颜色,而不是为每个顶点数组条目推送一种颜色(= 每个坐标 1 个),但要确保 geometry.color 属性的数组长度保持不变。

通过这 2 项更改,组合网格的结果看起来与为每个单元格创建单独网格时的结果完全相同。性能提升令人印象深刻。

单独的网格:

  • 渲染一帧需要 60 - 65 毫秒
  • 144 mb 已分配内存

组合网格:

  • 渲染一帧需要 0 - 1 毫秒
  • 58 mb 已分配内存

以下是固定片段:

 addCellGeometryToMapGeometry(cell) {

    let startIndex = this.mapVertices.length / 3;
    let cellIndices = cell.indices.length;
    let cellVertices = cell.vertices.length;

    console.log('Vertex-- maplength: ' + startIndex + ' celllength: ' + cellVertices);
    console.log('Indices -- maplength: ' + this.mapIndices.length + ' celllength: ' + cellIndices);
    console.log({cell});

    let color = new THREE.Color( Math.random(), Math.random(), Math.random() );

    for (let i = 0; i < cellIndices; i++) {
        this.mapIndices.push(startIndex + cell.indices[i]);
    }
    for (let i = 0; i < cellVertices; i++) {
        this.mapVertices.push(cell.vertices[i]);
        if (i % 3 === 0) { this.mapColors.push (color); }
    }

}


 generateMapMesh() {
    let geometry = new THREE.BufferGeometry();
    const material = new THREE.MeshPhongMaterial( {
        side: THREE.DoubleSide,
        flatShading: true,
        vertexColors: true,
        shininess: 0
    } );

    geometry.setIndex( this.mapIndices );
    geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( this.mapVertices, 3 ) );
    geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( new Float32Array(this.mapVertices.length), 3 ) );

    for ( let i = 0; i < this.mapColors.length; i ++ ) {
        geometry.attributes.color.setXYZ(i,  this.mapColors[i].r,  this.mapColors[i].g,  this.mapColors[i].b);
    }

    return new THREE.Mesh( geometry, material );
}