使用Three.js提高性能

4

我目前正在编写我的第一个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个回答

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个顶点,您希望每17毫秒使用CPU更新它们。请注意,您没有使用任何GPU优势。应该如何处理:

  • 首先,创建缓冲区,用于顶点位置、法线、纹理坐标、索引等。这些缓冲区合在一起称为网格。
  • 然后创建模型。模型由一个或多个网格组成,以及modelViewMatrix。这是非常重要的部分,矩阵4x4是该模型位置、旋转和缩放的代数表示。
  • 每次渲染时,在顶点着色器中执行以下操作:

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

    这来自您的cg / shaders / VerticalTiltShiftShader.js

  • 如果要旋转您的平面,则不需要乘以每个顶点,而只需一次乘以模型矩阵(使用THREE.js函数的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。


1
将镶嵌平面进行细分,对纹理进行噪声渲染,然后在顶点着色器中使用它来偏移你的顶点(texture2DLod)。要将事物返回到CPU,您可以从GPU读取纹理,或者根据您的用例,在CPU和GPU之间使用共享噪声种子,并计算一个较小的噪声地图来更新您的空间结构。 - LJᛃ
抱歉回复晚了,我曾经使用过OpenGL,并且使用了MVP矩阵等通用方法。但是这个项目的问题在于,我想要一个低多边形的平面,根据其相邻位置动态更新每个顶点的高度。因此,每一帧我实际上都会更新每个顶点的位置,然后更新缓冲区。我认为一个主要的问题是每一帧都要更新64x64顶点缓冲区。在这个项目中我并不真正使用模型,因为物体并不真正移动而是变形。;) - ruhig brauner
@LJ_1102 一个想法是编写一个顶点着色器来更新顶点缓冲区,但我甚至不知道在WebGL中是否可能。 ;D该着色器必须执行以下步骤:
  • 做基本的事情(根据MVP和顶点位置、法线等计算顶点位置)
  • 根据顶点邻居计算新位置,并以某种方式将新位置写入另一个缓冲区
  • 渲染后,交换当前顶点缓冲区和新顶点缓冲区
我不知道这是否会提高性能。而且我也不知道如何实现这个想法...
- ruhig brauner
@ruhigbrauner,你所描述的听起来像是想要使用变换反馈,可惜在WebGL中并不支持这个功能。不过,每帧在CPU上更新一个64x64的顶点缓冲区绝对足够快速。 - LJᛃ
嗯,所以在提高性能方面我已经没有什么选择了吗?我看到应用程序在笔记本电脑上运行时,每秒只能运行一个帧,... 最后的步骤将包括调整分辨率、跳过后处理、减少顶点等。 - ruhig brauner
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接