对于使用 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];
如何使缩放
、旋转
和变换
功能更快?取决于语言或数学。
最佳答案
将所有 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 ... }
然后,您只需上传
globalVertData
和gl.bufferData
,而不是每个单独 Sprite 的vertexData
。我有一种直觉,第二个比第一个更快。还需要 尽管有一个数组对象而不是一个数组 View ,但内存较少。也就是说,我没有测试过,所以我可能是错的。
摆脱
Z
。假设你不需要Z
Sprite 来摆脱它(而且看起来你不需要,因为旋转和平移都不会操纵 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.length
或foo.bar
中一样。 在第一个示例中,.
运算符每次迭代都会发生。在里面 其次,每个循环发生一次。在第三个中,这种情况根本不会发生。let
比var
慢let creates a new object every iteration of the loop. Var does not但如果你把它从循环中拉出来,问题就会消失。浏览器将来可能会解决这个问题 他们可以分析代码,发现不需要每次迭代都创建一个新对象(Spidermonkey 似乎会这样做,Chrome 60 似乎也解决了这个问题。Safari 仍然更慢)
其他通常有助于 Sprite 的东西。使用texture atlas 。这样您就可以通过一次绘制调用绘制所有 Sprite 。
您可能会发现 this answer也很有用
关于javascript - 在CPU上预计算顶点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43852943/