为了使用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更快的可能性是什么?取决于语言或数学。

最佳答案

将所有精灵的所有精灵数据放在一个数组中。 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

我有一个直觉,第二个比第一个要快。还需要
更少的内存,因为尽管有一个数组对象,但每个视图只有一个数组视图。那就是说我没有测试它,所以我可能是错的。
摆脱Z。假设您不需要Z来消除精灵(看起来好像不需要,因为既不旋转也不平移操纵z)。然后,您上传的数据更少了,至少scale会变得更快。我在上面做了。
从循环中拉出长度

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一样。
在第一个示例中,.运算符在每次迭代时发生。在里面
第二,每个循环发生一次。在第三次中根本没有发生。
let慢于var

let creates a new object every iteration of the loop. Var does not尽管将其拉出循环该问题将消失。浏览器将来可能会解决此问题
他们可以分析代码,并看到每次迭代都不需要创建新对象(Spidermonkey似乎可以做到这一点,Chrome 60似乎也可以解决此问题。Safari的运行速度仍然很慢)
通常有助于精灵的其他东西。使用texture atlas。这样,您可以通过一次绘制调用来绘制所有精灵。


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

09-27 15:19