使用 onMouseMove 和 RayCaster 的 Three.js 性能非常慢

use*_*161 2 javascript three.js

我正在用three.js 构建一个应用程序,但是我在性能方面遇到了真正的问题。这部分应用程序基于Voxel Painter 示例。在我的版本中,用户点击一个单元格开始放置,将光标拖到他们希望结束放置的位置,然后点击结束。

function onDocumentMouseMove(event) {
    //set up mouse and raycaster
    event.preventDefault();
    mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
    raycaster.setFromCamera(mouse, camera);

    switch (buildMode) {
        case buildModes.CORRIDOR:
            scene.add(rollOverFloor);

            var intersects = raycaster.intersectObjects(gridObject);
            if (intersects.length > 0) {

                var intersect = intersects[0];

                if (beginPlace == true) {
                    //store the intersection position
                    var endPlace = new THREE.Vector3(0, 0, 0);
                    endPlace.copy(intersect.point).add(intersect.face.normal);
                    endPlace.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
                    endPlace.set(endPlace.x, 0, endPlace.z);

                    corridorDrag(endPlace);

                }
                //if user hasn't begun to place the wall
                else {
                    //show temporary wall on grid
                    rollOverFloor.position.copy(intersect.point).add(intersect.face.normal);
                    rollOverFloor.position.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
                    rollOverFloor.position.set(rollOverFloor.position.x, 0, rollOverFloor.position.z);
                }
            }
            break;

    }
    render();
}
Run Code Online (Sandbox Code Playgroud)

当用户移动鼠标时调用上面的代码(主应用程序中有很多构建模式,但我没有在这里包括它们)。这个函数只是获取一个起点和终点,走廊拖动()函数填充起点和终点之间的单元格:

function corridorDrag(endPlace) {
    deleteFromScene(stateType.CORRIDOR_DRAG);

    var startPoint = startPlace;
    var endPoint = endPlace;
    var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
    var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);

    var differenceZ = Math.abs(startPlace.z - zIntersect.z);
    var differenceX = Math.abs(startPlace.x - xIntersect.x);

    var mergedGeometry = new THREE.Geometry();

    for (var i = 0; i <= (differenceZ / step); i++) {
        for (var j = 0; j <= (differenceX / step); j++) {
            var x = startPlace.x;
            var y = startPlace.y;
            var z = startPlace.z;

            if (endPoint.x <= (startPlace.x )) {
                if (endPoint.z <= (startPlace.z)) {
                    x = x - (step * j);
                    z = z - (step * i);
                }
                else if (endPoint.z >= (startPlace.z)) {
                    x = x - (step * j);
                    z = z + (step * i);
                }
            } else if (endPoint.x >= (startPlace.x)) {
                if (endPoint.z <= (startPlace.z)) {
                    x = x + (step * j);
                    z = z - (step * i);
                }
                else if (endPoint.z >= (startPlace.z)) {
                    x = x + (step * j);
                    z = z + (step * i);
                }
            }

            floorGeometry.translate(x, y, z);
            mergedGeometry.merge(floorGeometry);
            floorGeometry.translate(-x, -y, -z);
        }
    }

    var voxel = new THREE.Mesh(mergedGeometry, tempMaterial);
    voxel.state = stateType.CORRIDOR_DRAG;

    scene.add(voxel);
    tempObjects.push(voxel);
}
Run Code Online (Sandbox Code Playgroud)

首先,deleteFromScene() 函数从场景中删除所有当前突出显示的单元格(见下文)。然后(我相信)代码应该根据起点和终点创建许多网格,并将它们添加到场景中。

 function deleteFromScene(state) {
    tempObjects = [];
    var i = scene.children.length;
    while (i--) {
        if (scene.children[i].state != undefined)
            if (scene.children[i].state == state)
                scene.children.splice(i, 1);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如我所说,它非常非常缓慢。它似乎还向渲染器添加了大量的顶点,如 WebGLRenderer 统计窗口中所示。我不知道为什么它添加了这么多顶点,但我假设这就是它渲染如此缓慢的原因。

可以在此处查看应用程序- 通过单击一个单元格,将光标拖动到网格的另一端,并观察填充单元格所花费的时间,可以看到问题。

提前谢谢你,这真的是最后的手段。

Bra*_*ard 6

几年前,Twitter 发布了更新。在这次更新中,他们刚刚引入了无限滚动,在发布当天,该更新使用户浏览器崩溃。Twitter 工程师做了一些调查,发现崩溃是滚动事件每秒触发数百次的结果。

鼠标事件每秒可以触发很多次,并可能导致您的代码过于频繁地执行,这会减慢浏览器的速度并(在许多情况下)使其崩溃。Twitter(希望是你)的解决方案很简单:投票你的活动。

在您的 mousemove 事件处理程序中,检查自上次移动事件以来已经过去了几毫秒。

var lastMove = Date.now();

function onDocumentMouseMove(event) {
    if (Date.now() - lastMove < 31) { // 32 frames a second
        return;
    } else {
        lastMove = Date.now();
    }

    // your code here
}
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助!