javascript - 在CPU上预计算顶点

标签 javascript arrays performance webgl

对于使用 WebGL 渲染许多 Sprite ,我想我会询问性能。

让我们考虑一下:

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())

具有以下缩放旋转变换函数

 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];

如何使缩放旋转变换功能更快?取决于语言或数学。

最佳答案

  1. 将所有 Sprite 的所有 Sprite 数据放入一个数组中。 AFAICT 您为每个 Sprite 使用一个数组。这意味着您必须分别为每个 Sprite 的 vertexData 调用 gl.bufferData 。这比通过一次上传来上传所有 Sprite 的所有 Sprite 数据的一次调用要慢。

    如果你仍然想为每个 Sprite 保留一个对象,你可以让每个 Sprite 使用一个更大的全局数组的偏移量

    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 ...
    }
    

    然后,您只需上传 globalVertDatagl.bufferData,而不是每个单独 Sprite 的 vertexData

    我有一种直觉,第二个比第一个更快。还需要 尽管有一个数组对象而不是一个数组 View ,但内存较少。也就是说,我没有测试过,所以我可能是错的。

  2. 摆脱Z。假设你不需要 Z Sprite 来摆脱它(而且看起来你不需要,因为旋转和平移都不会操纵 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 中一样。 在第一个示例中,. 运算符每次迭代都会发生。在里面 其次,每个循环发生一次。在第三个中,这种情况根本不会发生。

  4. letvar

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

  5. 其他通常有助于 Sprite 的东西。使用texture atlas 。这样您就可以通过一次绘制调用绘制所有 Sprite 。

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

关于javascript - 在CPU上预计算顶点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43852943/

相关文章:

php - 如何在php/mysql中高效加载相关对象?

android - android Listview中多个点之间的距离

javascript - 检测到按下多个键以移动 "character/player"

javascript - 在页面加载 jQuery 时检索导航类名称

javascript - jQuery Datatable - 来自 ajax 调用的错误消息

javascript - 视频播放循环播放另一个ja

c++ - VC++2010 似乎只在固定数组中分配一个 std::string

c - C 中数字数组的排序

ruby - 如何将文本文件读入数组数组(每个子数组都是文本文件中的一行?)

Mysql - 有没有更好的方法来运行大量数据的子查询?