使用Three.js提高性能

时间:2015-01-28 16:06:34

标签: javascript performance three.js

我目前正在编写我的第一个Three.js / WebGL应用程序,它在我的PC(Chrome)上运行得非常好。可悲的是,在许多其他PC上,帧速率通常会低于每秒30帧。

由于应用程序实际上并不复杂,我想询问一些与应用程序相关的提示,以提高性能。 该应用程序的一个版本可以在这里找到:

www.wrodewald.de/StackOverflowExample /

应用程序包含一个使用64²顶点的动态(变形)平面。矩阵用于存储静态高度图和波形图。每个帧都会更新波形图以重新计算自身,一些滤波器用于与其顶点相比“均匀”每个顶点。 因此,必须更新平面的每个帧(颜色和顶点位置),这可能是性能问题的原因

第二个对象(菱形)不应该是一个问题,静止,移动一点但没什么特别的。

有三种灯光(环境光,方向光,球面光),无阴影,倾斜移位着色器和晕影着色器。

以下是每帧调用的函数:

var render = function() {
    requestAnimationFrame( render );
    var time = clock.getDelta();

    world.updateWorld(time);
    diamond.rotate(time);
    diamond.update(time);
    control.updateCamera(camera, time);
    composer.render();      
    stats.update();
}

这是world.updateWorld(time)做的

//in world.updateWorld(time) where
// accmap: stores acceleration and wavemap stores position
// this.mapSize stores the size of the plane in vertices (64)

// UPDATE ACCELERATION MAP
for(var iX = 1; iX < (this.mapSize-1); iX++) {
    for(var iY = 1; iY < (this.mapSize-1); iY++) {
        accmap[iX][iY] -=  dT * (wavemap[iX][iY]) * Math.abs(wavemap[iX][iY]);
    }   
}


// SMOOTH ACCELERATION MAP
for(var iX = 1; iX < (this.mapSize-1); iX++) {
    for(var iY = 1; iY < (this.mapSize-1); iY++) {
        tempmap[iX][iY] =     accmap[iX-1][iY-1]    * 0.0625
                            + accmap[iX-1][iY  ]    * 0.125
                            + accmap[iX-1][iY+1]    * 0.0625
                            + accmap[iX  ][iY-1]    * 0.125
                            + accmap[iX  ][iY  ]    * 0.25
                            + accmap[iX  ][iY+1]    * 0.125
                            + accmap[iX+1][iY-1]    * 0.0625
                            + accmap[iX+1][iY  ]    * 0.125
                            + accmap[iX+1][iY+1]    * 0.0625;

        accmap[iX][iY] = tempmap[iX][iY];
    }
}


// UPDATE WAVE MAP
for(var iX = 1; iX < (this.mapSize-1); iX++) {
    for(var iY = 1; iY < (this.mapSize-1); iY++) {
        wavemap[iX][iY] += dT * accmap[iX][iY];
    }   
}

for(var i = 0; i < this.mapSize; i++) {
    for(var k = 0; k < this.mapSize; k++) {
        geometry.vertices[ i * this.mapSize + k ].y =  wavemap[i][k] + heightmap[i][k];
    }   
}

for(var i = 0; i < geometry.faces.length; i++) {

    var vertexA = geometry.vertices[geometry.faces[i].a];
    var vertexB = geometry.vertices[geometry.faces[i].b];
    var vertexC = geometry.vertices[geometry.faces[i].c];

    var val = (vertexA.y + vertexB.y + vertexC.y) / 3.0;
    val = (val / 200.) + 0.5;

    geometry.faces[i].color.r = val;
    geometry.faces[i].color.g = val;
    geometry.faces[i].color.b = val;
}

geometry.colorsNeedUpdate = true;
geometry.verticesNeedUpdate = true;

这些是“钻石”功能

this.rotate = function(dT) {
    counter += 0.5 * dT;
    counter % 1;
    var x = 0.0025 * (Math.sin((counter) * 2 * Math.PI));
    var y = 0.0025 * (Math.cos((counter) * 2 * Math.PI));
    this.mesh.rotateOnAxis(new THREE.Vector3(1,0,0), x);
    this.mesh.rotateOnAxis(new THREE.Vector3(0,0,1), y);
}

this.update = function(dT) {
    for(var i = 0; i < geometry.faces.length; i++) {    
        geometry.faces[i].color.lerp(color, dT*(0.9));
    }

    geometry.colorsNeedUpdate = true;
}

您是否发现帧率如此不一致的任何原因?

1 个答案:

答案 0 :(得分:5)

修改

我发现了你需要改进的两件大事:

使用GPU更新平面

加速:高

让我们从plane.js中选择您的代码

    timer += dT;
    if(timer > 0.1) {           
        var x = 2 + Math.floor(Math.random() * (this.mapSize - 4));
        var y = 2 + Math.floor(Math.random() * (this.mapSize - 4));
        //accmap[x][y] += 30000 * Math.random () - 15000
    }


    // UPDATE ACCELERATION MAP
    for(var iX = 1; iX < (this.mapSize-1); iX++) {
        for(var iY = 1; iY < (this.mapSize-1); iY++) {
            accmap[iX][iY] -=  dT * (wavemap[iX][iY]) * Math.abs(wavemap[iX][iY]);
        }   
    }

所以你有4096个顶点,你想用CPU每17毫秒更新一次。请注意,您没有使用任何GPU优势。应该怎么做:

  • 首先为顶点位置,法线,纹理坐标,索引......创建缓冲区。这一起称为网格。
  • 然后你创建一个模型。模型由一个或多个网格组成。和modelViewMatrix。这是非常重要的部分,矩阵4x4是该模型的位置,旋转和比例的代数表示。
  • 每次渲染都会在顶点着色器中完成此操作:

    "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

    来自你的cg / shaders / VerticalTiltShiftShader.js

  • 如果要旋转平面,则不要将每个顶点相乘,但只需将模型矩阵乘以一次(js与THREE.js函数):

    projectionMatrix.makeRotationY(dT);

    然后每个顶点在顶点着色器中与此矩阵相乘,这样更快。

Javascript风格

加速:无 - 中等,但它可以让您更快地编码

让我们以你的plane.js为例。

// this is your interface
function PlaneWorld () {
    this.init = function() {};
    this.updateVertices = function() {};
    this.updateWorld = function(dT) {};
    // ... and more
}

// and somewhere else:
var world = new PlaneWorld();

如果您的项目中只有一个平面,您可以将其视为单例并且实现正常。但是,如果要创建2个或更多平面,则会为每个实例(new PlaneWorld())重新创建所有函数。正确的方法是:

function PlaneWorld () {
    ...
}

PlaneWorld.prototype.init = function() {};    
PlaneWorld.prototype.updateVertices = function() {};    
PlaneWorld.prototype.updateWorld = function(dT) {};  
// ... and more

var world = new PlaneWorld();

// calling methods works same
world.updateVertices();

或更复杂的匿名函数版本:

var PlaneWorld = (function() {

    // something like private static variables here

    var PlaneWorld = function () {
        ...
    }

    PlaneWorld.prototype = {
        init: function() {},
        updateVertices: function() {},
        updateWorld: function(dT) {} 
        // ... and more
    }

    return PlaneWorld();
})();

var world = new PlaneWorld();
// calling methods works same
world.updateVertices();

然后降低新实例成本。现在可能连接的东西,每个实例应共享相同的网格,但有自己的modelViewMatrix。