预先计算CPU上的顶点

时间:2017-05-08 16:40:24

标签: javascript arrays performance webgl

为了使用WebGL渲染许多精灵,我想我会问性能。

让我们考虑一下:

for(let i = 0; i < 50000; i++){
    let t = new Sprite();
    t.scale(Math.random())
    t.rotate(Math.random())
    t.transform(Math.random(),Math.random())

scalerotatetransform

具有以下功能
 translate(x, y) {
    for (let i = 0; i < this.vertexData.length; i += 3) {
      this.vertexData[i] += x;
      this.vertexData[i + 1] += y;
    }
  }


  rotate(alpha) {
    this.translate(-this.position[0], -this.position[1]);
    for (let i = 0; i < this.vertexData.length; i += 3) {
      let new_x = this.vertexData[i] * Math.cos(alpha) - this.vertexData[i + 1] * Math.sin(alpha);
      let new_y = this.vertexData[i + 1] * Math.cos(alpha) + this.vertexData[i] * Math.sin(alpha);
      this.vertexData[i] = new_x;
      this.vertexData[i + 1] = new_y;
    }
    this.translate(this.position[0], this.position[1]);
  }


  scale(factor) {
    this.translate(-this.position[0], -this.position[1]);
    for (let i = 0; i < this.vertexData.length; i += 3) {
      this.vertexData[i] *= factor;
      this.vertexData[i + 1] *= factor;
    }
    this.translate(this.position[0], this.position[1])
  }

this.vertexData=[-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0];

有什么方法可以让函数scalerotatetransform更快?取决于语言或数学。

1 个答案:

答案 0 :(得分:1)

  1. 将所有精灵的所有精灵数据放在一个数组中。 AFAICT你每个精灵使用一个数组。这意味着你必须分别为每个精灵的vertexData调用gl.bufferData。这比仅通过一次上传为所有精灵上传所有精灵数据的一次调用慢。

    如果你仍然希望每个精灵保留一个对象,你可以让每个精灵使用一个偏移到一个更大的全局数组

    const maxSprites = 50000;
    const globalVertData = new Float32Array(maxSprites * 12);
    const quad = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
    const numSprites = 0;
    
    class Sprite {
      constructor() {
        const offset = numSprites++ * 12 * 4;
        // make vertexData a view into the larger array
        this.vertexData = new Float32Array(
            globalVertData.buffer, offset, 12);
        this.vertexData.set(quad); 
      }
      ... your functions from above ...
    }
    

    或者

    const maxSprites = 50000;
    const globalVertData = new Float32Array(maxSprites * 12);
    const quad = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
    const numSprites = 0;
    
    class Sprite {
      constructor() {
        this.offset = numSprites++ * 12;
        globalVertData.set(quad, this.offset);
      }
      translate(x, y) {
        let i = this.offset;
        const end = i + 12;
        for (; i < end; i += 2) {
          globalVertData[i] += x;
          globalVertData[i + 1] += y;
        }
      } 
      ... functions for rotate and scale that use this.offset ...
    }
    

    然后,您只需使用globalVertData上传gl.bufferData,而不是每个精灵的vertexData

    我有直觉,第二个比第一个更快。它也需要 内存较少,因为每个spite都有一个数组对象而不是一个数组视图。那说我没有测试,所以我错了。

  2. 摆脱Z。假设你不需要Z精灵就可以摆脱它(并且它似乎没有,因为既不旋转也不翻译操纵z)。然后,您上传的数据较少,至少scale会更快。我在上面做了这个。

  3. 从循环中拉出长度

    for (let i = 0; i < someArray.length; ++i) {
      ...
    

    慢于

    const len = someArray.length;
    for (let i = 0; i < len; ++i) {
      ...
    

    这也比

    const spriteLen = 12;  // GLOBAL OR CLOSED VARIABLE
    
    const len = spriteLen;
    for (let i = 0; i < len; ++i) {
      ...
    

    基本上.运算符需要时间,如array.lengthfoo.bar。 在第一个示例中,每次迭代都会发生.运算符。在里面 第二,它每循环发生一次。在第3天它根本不会发生。

  4. let慢于var

    let creates a new object every iteration of the loop. Var does not虽然如果你把它拉出循环,问题就会消失。可能的浏览器将来会解决这个问题 他们可以分析代码并看到他们不需要每次迭代都创建一个新对象(Spidermonkey似乎这样做,Chrome 60似乎也解决了这个问题.Safari它仍然较慢)

  5. 其他通常有助于精灵的事情。使用texture atlas。这样你就可以通过一次平局调用绘制所有精灵。

  6. 您可能会发现this answer也很有用