Three.js 在 100 个立方体动画上崩溃

Posted

技术标签:

【中文标题】Three.js 在 100 个立方体动画上崩溃【英文标题】:Three.js crashing on 100 cube animation 【发布时间】:2015-02-21 13:20:12 【问题描述】:

在我的场景中,我有 4 个点光源,其中 3 个连接在相机上,还有大约 100 到 300 个立方体。 我有许多类别的立方体,每个类别介于 100 到 300 之间。根据用户菜单选择,我的场景中可能随时出现 1 个类别的立方体。

(100 个立方体的类别)renderer.info:

memory: 
  Objectgeometries: 2
  programs: 3
  textures: 100
render: 
  calls: 203
  faces: 1360
  points: 0
  vertices: 4080

在一个循环中,我为每个类别生成我的立方体,如下所示:

var materials = [   
    backgroundMaterial,
    backgroundMaterial,
    backgroundMaterial,
    backgroundMaterial,
    productMaterial,
    backgroundMaterial
];

var cubeMaterial = new THREE.MeshFaceMaterial( materials );

var object3D = new THREE.Mesh( geometryBox, cubeMaterial );

材料backgroundMaterial在循环外定义一次;

var backgroundMaterial = new THREE.MeshPhongMaterial(
    color: this.options.models.boxColor,
    specular: this.options.models.boxSpecular,
    //emissive : 0xefefef,
    //side: THREE.DoubleSide,
    overdraw: false,
    transparent: false,
    metal:true,
    shininess: this.options.models.boxShininess,
    reflectivity: this.options.models.boxReflectivity,
    fog:false
);

每次循环都在循环中使用 productMaterial,因为每个立方体的纹理都不同。

var productMaterial = new THREE.MeshBasicMaterial(
    map: productModelTexture,
    color: that.options.models.boxColor,
    specular: that.options.models.boxSpecular,
    //emissive : 0xefefef, 
    side: THREE.FrontSide,
    overdraw: false,
    transparent: false,
    metal:true,
    shininess: that.options.models.textureShininess,
    reflectivity: that.options.models.textureReflectivity,
    opacity: 1,
    fog:false
);

另外我此时没有将网格添加到场景中,它们被设置为visible = false

之后,我将我的立方体推入一个数组对象中,该对象内的每个数组都是一个长度在 100 到 300 之间的立方体类别。

当我的应用程序启动时,我会运行一个类似于下面的动画,它将一类立方体带入场景。

helix : function( category ) 

    if ( this.models[category] && this.models[category].length > 0 )               

        TWEEN.removeAll();

        new TWEEN.Tween( this.camera.position ).to( x:0,y:0,z:90000, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start(); 
        new TWEEN.Tween( this.camera.rotation ).to( x:0,y:0,z:0, 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start();             

        this.models.reset( category );

        for ( var i in this.models[category] ) 

            var model = this.models[category][i];
                model.visible = true;
                this.scene.add( model );

            new TWEEN.Tween( model.position ).to(
                x: model.helix.position.x,
                y: model.helix.position.y,
                z: model.helix.position.z
            , randBtwn( 1000, 3000 ) ).easing( TWEEN.Easing.Exponential.InOut ).delay( 1001 ).start();

            new TWEEN.Tween( model.rotation ).to( 
                x: model.helix.rotation.x,
                y: model.helix.rotation.y,
                z: model.helix.rotation.z
            , randBtwn( 1000, 3000 ) ).easing( TWEEN.Easing.Exponential.InOut ).delay( 1001 ).onComplete(function()

            ).start();
        

    

.bind( that )

此外,您会注意到 helix 内部的另一个函数调用,这个:this.models.reset( category );

下面的代码实质上是重置对象位置并将它们设置为visible = false,最后将它们从场景中移除。

reset : function( category, callback ) 

    for ( var j in this.models ) 

        if ( this.models[j] instanceof Array && this.models[j].length > 0 && category !== j ) 

            for ( var i in this.models[j] ) 

                var model = this.models[j][i];
                    model.visible = true;

                new TWEEN.Tween( model.position ).to(
                    x: model.outside.position.x,
                    y: model.outside.position.y,
                    z: model.outside.position.z
                , 1000 ).easing( TWEEN.Easing.Exponential.InOut ).start();

                new TWEEN.Tween( model.rotation ).to( 
                    x: model.outside.rotation.x,
                    y: model.outside.rotation.y,
                    z: model.outside.rotation.z
                , 1000 ).easing( TWEEN.Easing.Exponential.InOut ).onComplete(function ( m )

                    m.visible = false;
                    this.scene.remove( m );

                    if ( callback ) 
                        callback();
                    

                .bind( that, model )).start();
            

        

    

.bind( that )  

在一台电脑上,一切都很顺利,我以 36 fps 的速度运行。我的 gpu 是新的 nvidia GTX(不过我不知道 36 是否可以接受)。

问题是,当我尝试使用最新的 chrome 在我的 nexus 5 上运行我的应用程序时,我在离开场景的多维数据集和其他进入的多维数据集之间的过渡之间会出现巨大的 fps 损失。大多数情况下,这会导致chrome 崩溃...除此之外,如果我不更改类别并且没有播放动画,它在我的手机上运行正常。

PS:我无法合并几何图形,因为每个网格都必须在用户选择时自行移动。(如果我没有记错的话)

这种性能下降/崩溃的原因可能是什么?如果您在场景外移动 200 个立方体并在场景内移动另外 200 个立方体,您将如何处理类似的场景?有什么我应该考虑的提示吗,请记住我还是three.js的新手。

需要任何其他可能是原因的来源,请告诉我,我会更新我的问题。

【问题讨论】:

【参考方案1】:

首先,36 fps 是可以接受的。根据经验,我使用 25 fps 作为最低要求。

现在解决问题。 nexus 5 的 gpu 比您的 PC 慢得多。由于着色器直接传递给 GPU,因此速度很重要。当您尝试在预算 PC 上玩《孤岛危机》时,同样的问题是,GPU 功能不够强大,无法足够快地处理所有数据。

可能是一种将所有立方体几何图形添加到单个网格的解决方案,可能使用THREE.MeshFaceMaterial 将不同的材料应用于每个立方体。处理具有 100 个几何形状的单个网格比处理具有 1 个几何形状的 100 个网格要快。但正如我所说。也可能这根本没有解决任何问题,它更像是一个冰雹玛丽。

编辑:在单个网格中添加几何图形并检测单击的几何图形。

我想了想,可以检测到点击的几何图形。它不漂亮,但它可能会给你一些关于如何做得漂亮的想法。 只需向几何体的面添加另一个属性。

var addGeometry = function(baseGeometry, addedGeometry)
    for(i in addedGeometry.faces)
        addedGeometry.faces[i].parent = addedGeometry;
    
    baseGeometry.add(addedGeometry);

那么,在进行光线投射时,您不会调用object 属性,而是调用face.parent 属性,它应该包含被点击的几何体,您可以随意操作。

但同样,我不知道这将如何在性能方面发挥作用。不过值得一试。

您可以尝试使用webworkers。

【讨论】:

我也考虑过这一点,合并几何,但问题是我需要在单击/用户选择时为每个立方体设置单独的动画。所以我想如果我要合并它们,如果用户点击它,我将无法为 1 个立方体设置动画。 动画不会有太大问题,您只需调整几何体相对于网格的位置即可。点击就另当别论了,可以得到距离、人脸、faceIndex、物体和点,可惜,不是已经点击的物体子。你可以检查什么面属于什么几何,但我担心这只会导致比它解决的问题更多的问题。 您仍然可以为合并几何体中的立方体顶点设置动画。 是的,就像我说的那样,这不是问题。检测合并几何图形的哪个子元素被点击。

以上是关于Three.js 在 100 个立方体动画上崩溃的主要内容,如果未能解决你的问题,请参考以下文章

three.js 在悬停 LineSegmentsGeometry 上突出显示立方体的边缘

第136篇:Three.js基础入门动画API:setInterval 与 requestAnimationFrame的区别

Three.js 进阶之旅:新春特典-Rabbit craft go 🐇

如何在轴 three.js 上旋转 3D 对象?

three.js - 相机变化的非常奇怪的行为

带有骨骼动画(搅拌机导出)的模型在三个.js 中动画不正确