【问题标题】:HTML5 Canvas lagsHTML5 画布滞后
【发布时间】:2017-07-15 22:04:08
【问题描述】:

我最近在我的页面中添加了一个画布元素,球体上的随机点。它在 PC 上运行良好,但在手机和平​​板电脑上渲染非常慢。 如何加速球体并减少滞后? 任何帮助将不胜感激。GitHub example

【问题讨论】:

  • 您似乎只是在推动移动设备的边界。要提高速度,您必须优化代码或降低图形质量。例如,如果update 需要render,您可以通过注册进行优化,如果没有移动,可能会节省资源。您还可以将画布的分辨率减半,以指数方式减少要计算的像素数量。 (之后你用 css 缩放画布,虽然这可能会使它变得模糊)
  • 感谢您的支持。我会尝试使用“更新”和“渲染”。

标签: javascript html5-canvas


【解决方案1】:

还有很大的改进空间。

在你的渲染循环中

    for (var p of points) {
        p = rotation.multiplyVector(p);
        ctx.beginPath();
        ctx.arc(p.x + c.width / 2, p.y + c.height / 2, 2, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.fill();
    }

如果您一遍又一遍地渲染相同的样式,则不需要 beginPath

    ctx.beginPath();
    for (var p of points) {
        p = rotation.multiplyVector(p);
        const x = p.x + c.width / 2;
        const y = p.y + c.height / 2;
        ctx.moveTo(x + 2, y)
        ctx.arc(x, y, 2, 0, 2 * Math.PI);
    }
    ctx.fill();

然后矩阵向量乘法有不必要的审查和不必要的内存分配和对象实例化

你有

Matrix3.prototype.multiplyVector = function (vec) {
    if (vec instanceof Vector3) {
        var x = this.data[0 + 0 * 3] * vec.x + this.data[0 + 1 * 3] * vec.y + this.data[0 + 2 * 3] * vec.z;
        var y = this.data[1 + 0 * 3] * vec.x + this.data[1 + 1 * 3] * vec.y + this.data[1 + 2 * 3] * vec.z;
        var z = this.data[2 + 0 * 3] * vec.x + this.data[2 + 1 * 3] * vec.y + this.data[2 + 2 * 3] * vec.z;

        return new Vector3(x, y, z);
    }
}

这不会发生if (vec instanceof Vector3) { ???不在您的代码中,所以为什么要浪费 CPU 时间呢。

然后this.data[2 + 0 * 3] 优化器可能会为您提供此功能,但手机的优化不如台式机,我不确定这是否会被采纳。还有一些浏览器在使用间接引用时速度较慢 this.data[?]data[?]

为每个圆圈创建一个新向量以立即丢弃它根本不利于内存。您只需要一个对象,因此请将其传递给要设置的函数。

改进

Matrix3.prototype.multiplyVector = function (vec, retVec = new Vector3(0,0,0)) {
    const d = this.data;
    retVec.x = d[0] * vec.x + d[3] * vec.y + d[6] * vec.z;
    retVec.y = d[1] * vec.x + d[4] * vec.y + d[7] * vec.z;
    retVec.z = d[2] * vec.x + d[5] * vec.y + d[8] * vec.z;
    return retVec;
};

然后在循环中

const rp = new Vector3(0,0,0);
ctx.beginPath();
for (var p of points) {
    rotation.multiplyVector(p,rp);
    const x = rp.x + c.width / 2;
    const y = rp.y + c.height / 2;
    ctx.moveTo(x + 2, y)
    ctx.arc(x, y, 2, 0, 2 * Math.PI);
}
ctx.fill();

Vector3Matrix3 对象都非常浪费内存,这意味着当您应该重用内存时,您无法控制 CPU 周期仅用于分配和删除。

您有 Matrix rotate 函数来构建旋转,该旋转创建一个新矩阵来创建一个旋转矩阵,然后需要一个新矩阵来相乘。您创建 6 个完整的矩阵对象只是为了获得一个矩阵。

rotate 函数以 x,y,z 中的 2 为 0 调用,这意味着许多乘法和加法只是变为零,或者您最终添加了 omc + cos 等于 1,或者您乘以 1 没有任何变化。

你有

Matrix3.rotate = function (angle, x, y, z) {
    var result = new Matrix3();
    result.setIdentity();

    var cos = Math.cos(angle);
    var sin = Math.sin(angle);
    var omc = 1 - cos;

    result.data[0 + 0 * 3] = x * omc + cos;
    result.data[1 + 0 * 3] = y * x * omc + z * sin;
    result.data[2 + 0 * 3] = x * z * omc - y * sin;

    result.data[0 + 1 * 3] = x * y * omc - z * sin;
    result.data[1 + 1 * 3] = y * omc + cos;
    result.data[2 + 1 * 3] = y * z * omc + x * sin;

    result.data[0 + 2 * 3] = x * z * omc + y * sin;
    result.data[1 + 2 * 3] = y * z * omc - x * sin;
    result.data[2 + 2 * 3] = z * omc + cos;

    return result;
}

通过直接乘以一个矩阵来创建一个旋转矩阵,每个轴都需要一个。

Matrix3.prototype.rotateX = function(angle, result = new Matrix3()) {
    const r = result.data;
    const d = this.data;
    const c = Math.cos(angle);
    const s = Math.sin(angle));
    const ns = -s;
    r[0] = d[0]
    r[1] = d[1] * c + d[2] * ns;
    r[2] = d[1] * s + d[2] * c;
    r[3] = d[3];
    r[4] = d[4] * c + d[5] * ns;
    r[5] = d[4] * s + d[5] * c;
    r[6] = d[6];
    r[7] = d[7] * c + d[8] * ns;
    r[8] = d[7] * s + d[8] * c;     
    return result;
},

对 rotateY,rotateZ 做同样的事情(每个都与上面不同)

实例矩阵直接设置标识而不需要第二次调用。

function Matrix3() { this.data = [1,0,0,0,1,0,0,0,1] }

设置身份

Matrix3.prototype.setIdentity = function () { 
    const d = this.data;
    d.fill(0);
    d[8] = d[4] = d[0] = 1;
}

然后在你的循环函数中可以访问两个矩阵对象。

const mat1 = new Matrix3();
const mat2 = new Matrix3();
const rp = new Vector3(0,0,0);
const MPI2 = 2 * Math.PI;
function loop(){
    mat1.setIdentity();
    mat1.rotateX(angle.x,mat2);
    mat2.rotateY(angle.y,mat1);
    mat1.rotateZ(angle.z,mat2);
    // your text rendering in here
    const cw = c.width / 2;
    const ch = c.height / 2;
    ctx.beginPath();
    for (var p of points) {
        mat2.multiplyVector(p,rp);
        const x = rp.x + cw;
        const y = rp.y + ch;
        ctx.moveTo(x + 2, y)
        ctx.arc(x, y, 2, 0, MPI2);
    }
    ctx.fill();
}

这会给你一点额外的速度。任何其他改进都将是特定于浏览器/设备的。

UPDATE 按照 cmets 的要求,以下 sn-p 包含绕 X、Y 和 Z 轴旋转的缩短旋转矩阵乘法

// How I find the optimum matrix multiplication via 
// eleminating a[?] * 0 = 0 
// reducing  a[?] * 1 = a[?]
//
// The following are the rotations for X,Y,Z as matrix
//-------------------
// rotate X
//  1       0     0
//  0  cos(r) sin(r)
//  0 -sin(r) cos(r)
//-------------------
// rotate Y
//  cos(r) 0 sin(r)
//  0      1      0 
// -sin(r) 0 cos(r)
//-------------------
// rotate Z
//  cos(r) sin(r) 0
// -sin(r) cos(r) 0 
//       0      0 1
// The matrix indexes 
// [0][1][2]
// [3][4][5]
// [6][7][8]
// Using the indexs and multiply is c = a * b
// c[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6]
// c[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7]
// c[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8]
// c[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6]
// c[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7]
// c[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8]
// c[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6]
// c[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7]
// c[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
// Then use the rotations matrix to find the zeros and ones 
// EG rotate X b[1],b[2],b[3],b[6] are zero and b[0] is one
// c[0] = a[0] * 1    + a[1] * 0    + a[2] * 0   
// c[1] = a[0] * 0    + a[1] * b[4] + a[2] * b[7]
// c[2] = a[0] * 0    + a[1] * b[5] + a[2] * b[8]
// c[3] = a[3] * 1    + a[4] * 0    + a[5] * 0   
// c[4] = a[3] * 0    + a[4] * b[4] + a[5] * b[7]
// c[5] = a[3] * 0    + a[4] * b[5] + a[5] * b[8]
// c[6] = a[6] * 1    + a[7] * 0    + a[8] * 0   
// c[7] = a[6] * 0    + a[7] * b[4] + a[8] * b[7]
// c[8] = a[6] * 0    + a[7] * b[5] + a[8] * b[8]
// then eliminate all the zero terms a[?] * 0 == 0 and 
// remove the 1 from 1 * a[?] = a[?]
// c[0] = a[0] 
// c[1] = a[1] * b[4] + a[2] * b[7]
// c[2] = a[1] * b[5] + a[2] * b[8]
// c[3] = a[3]   
// c[4] = a[4] * b[4] + a[5] * b[7]
// c[5] = a[4] * b[5] + a[5] * b[8]
// c[6] = a[6] 
// c[7] = a[7] * b[4] + a[8] * b[7]
// c[8] = a[7] * b[5] + a[8] * b[8]
// And you are left with the minimum calculations required to apply a particular rotation Or any other transform.

Matrix3.prototype.rotateX = function(angle, result = new Matrix3()) {
    const r = result.data;
    const d = this.data;
    const c = Math.cos(angle);
    const s = Math.sin(angle);
    const ns = -s;
    r[0] = d[0];
    r[1] = d[1] * c + d[2] * ns;
    r[2] = d[1] * s + d[2] * c;
    r[3] = d[3];
    r[4] = d[4] * c + d[5] * ns;
    r[5] = d[4] * s + d[5] * c;
    r[6] = d[6];
    r[7] = d[7] * c + d[8] * ns;
    r[8] = d[7] * s + d[8] * c;     
    return result;
}
Matrix3.prototype.rotateY = function(angle, result = new Matrix3()) {
    const r = result.data;
    const d = this.data;
    const c = Math.cos(angle);
    const s = Math.sin(angle);
    const ns = -s;
    r[0] = d[0] * c + d[2] * ns;
    r[1] = d[1];
    r[2] = d[0] * s + d[2] * c;
	
    r[3] = d[3] * c + d[5] * ns;
    r[4] = d[4];
    r[5] = d[3] * s + d[5] * c;
	
    r[6] = d[6] * c + d[8] * ns;
    r[7] = d[7];
    r[8] = d[6] * s + d[8] * c;     
    return result;
}
Matrix3.prototype.rotateZ = function(angle, result = new Matrix3()) {
    const r = result.data;
    const d = this.data;
    const c = Math.cos(angle);
    const s = Math.sin(angle);
    const ns = -s;
    r[0] = d[0] * c + d[1] * ns;
    r[1] = d[0] * s + d[1] * c;
    r[2] = d[2];
	
    r[3] = d[3] * c + d[4] * ns;
    r[4] = d[3] * s + d[4] * c;
    r[5] = d[5];
	
    r[6] = d[6] * c + d[7] * ns;
    r[7] = d[6] * s + d[7] * c;     
    r[8] = d[8];
    return result;
}

【讨论】:

  • Blindman67,我已经实现了你的所有想法,到目前为止看起来还不错:yovchenko.github.io/sphere 仅通过直接乘以矩阵来创建旋转矩阵对我来说太难了。你能帮我一个吗更多时间,好吗?显示代码示例
  • @WladimirYovchenko 我添加了 3 个旋转矩阵乘法和一些关于如何优化乘法的信息(请注意,它是手动输入的,没有运行,代码中可能有一些拼写错误)
猜你喜欢
  • 2012-10-24
  • 2020-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-22
相关资源
最近更新 更多