合并网格但保留单独的材料

合并网格但保留单独的材料

本文介绍了Three.js 合并网格但保留单独的材料的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在通过 Web 服务器开发一个可视化项目,我让它工作并查看我想要的样子,但它比我希望的要慢得多.基本上,它有一个巨大的网格用于对空间进行建模,然后各个立方体以不同的颜色显示在网格中(或者如果没有任何东西,则当前将其移除,但可以轻松地使用透明材料代替)来表示存在的可能性对象在网格的那个区域,并且颜色需要随着它接收到更多数据而动态改变(目前尚未实现,但此时很容易完成).以下是我目前正在运行的代码(名为 VFF.js):

I am working on a visualization project based through a web server and I have it working and looking how I want it but it is much slower than I was hoping. Basically, it has a huge grid for modeling a space and then individual cubes are displayed in the grid with different colors (or just removed currently if there is nothing there, but could easily just use transparent materials instead) to represent how likely there is an object in that area of the grid, and the colors need to change dynamically as it receives more data (not currently implemented but easily done at this point). Below is the code as I currently have it working (named VFF.js):

//dimensions in feet
var xFeet = 20;
var yFeet = 10;
var zFeet = 15;

var certaintyGrid = [];
var gridSize = 6; //6 inch squares (higher number = lower resolution)
var objectThreshhold = 5;

//change the dimesnions to blocks/grid spaces
var xDim = Math.ceil(xFeet * 12 / gridSize);
var yDim = Math.ceil(yFeet * 12 / gridSize);
var zDim = Math.ceil(zFeet * 12 / gridSize);

//parrot ar.drone is 22.4 x 22.4 x 5.4 inches
var droneWidth = 22.4 / gridSize;
var droneLength = 22.4 / gridSize;
var droneHeight = 5.4 / gridSize;


//get the canvas and set its background
var container = document.getElementById("VFFCanvas");
container.style.background = '#cccccc';


//create the scene, renderer, and camera and then put it in the VFFCanvas
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, container.width/container.height, 0.1, 1000);
var renderer = new THREE.WebGLRenderer( { antialias: false, canvas: container, alpha: true} );
renderer.setClearColor(new THREE.Color().setRGB( 0.95, 0.95, 0.95 ));
renderer.setSize(container.width, container.height);
renderer.enableScissorTest ( true );
document.body.appendChild(renderer.domElement);


//create the light source
var directionalLight = new THREE.DirectionalLight(0xffffff);
scene.add(directionalLight);


//create the drone object
var droneGeo = new THREE.CubeGeometry(droneWidth, droneHeight, droneLength);
var droneMat = new THREE.MeshLambertMaterial({color: 0x888888});
var drone = new THREE.Mesh(droneGeo, droneMat);
//drone.position.set(15,4,10);
drone.position.set(xDim / 2 - 1, 2, zDim / 2 - 1); //start the drone in the center of the grid close to the ground
scene.add(drone);


//maybe do a small far clipping for the top down so its easier to see things around you

//set up the camera and views
var chaseDistance = droneWidth * 8; //bigger = farther away
var chaseClipping = 1.2; //bigger = more not displayed
var topDownDroneScaledWidth = droneWidth * 15; //bigger # = farther away
var topDownHeightMargin = 4; //how many drones above or below it will cut off before it clips the squares away and not have them block the

                //view of where we are
var views = [{ //top down cam
    left: 0.505,
    bottom: 0.01,
    width: 0.485,
    height: 0.485,
    fov: 45,
    closeClip: topDownDroneScaledWidth - droneHeight * topDownHeightMargin,
    farClip: topDownDroneScaledWidth + droneHeight * topDownHeightMargin,
    //background: new THREE.Color().setRGB( 0.6, 0.2, 0.2 ),
    setup: function (camera) {
        camera.rotation.x = -Math.PI/2;
    },
    updateCamera: function (camera, scene) {
        //position it above the drone (need to be carefull if we are taking ceiling measurments or else our view will be blocked)
        camera.position.x = drone.position.x;
        camera.position.z = drone.position.z;
        camera.position.y = drone.position.y + topDownDroneScaledWidth; //this height shows a decent view based on the drones size
        camera.rotation.z = drone.rotation.y;   //use the z because we are looking straight down
    }
},{ //chase cam
    left: 0.01,
    bottom: 0.01,
    width: 0.485,
    height: 0.98,
    fov: 45,
    closeClip: chaseDistance * chaseClipping, //chaseDistance * sqrt(2) is our distance to the center of the drone
    farClip: 10000,
    //background: new THREE.Color().setRGB( 0.5, 0.5, 0.7 ),
    setup: function (camera) {},
    updateCamera: function (camera, scene) {
        //find out wheres behind the drone
        camera.position.x = drone.position.x + chaseDistance * Math.sin(drone.rotation.y);
        camera.position.z = drone.position.z + chaseDistance * Math.cos(drone.rotation.y);
        camera.position.y = drone.position.y + chaseDistance;

        //focus on the drone
        camera.lookAt(drone.position);
    }
},{ //cockpit cam
    left: 0.505,
    bottom: 0.505,
    width: 0.485,
    height: 0.485,
    fov: 45,
    closeClip: 0.1,
    farClip: 10000,
    //background: new THREE.Color().setRGB( 0.3, 0.7, 0.3 ),
    setup: function (camera) {
        drone.add(camera);
        camera.position.z = -droneLength / 2; //position it where the camera is on the ar drone
    },
    updateCamera: function (camera, scene) {}
}];

//initialize the views' cameras
for (var ii =  0; ii < views.length; ++ii ) {
    var view = views[ii];
    camera = new THREE.PerspectiveCamera( view.fov, container.width / container.height, view.closeClip, view.farClip );
    view.camera = camera;
    view.setup(camera);
    view.left   = Math.floor( container.width * view.left );
    view.bottom = Math.floor( container.height * view.bottom );
    view.width  = Math.floor( container.width * view.width );
    view.height = Math.floor( container.height * view.height );
}


//create the grid objects
var geometry = new THREE.CubeGeometry(0.9, 0.9, 0.9);
//var material = new THREE.MeshLambertMaterial({color: 0x0000ff, transparent: false, opacity: 0 });

for(i = 0; i < xDim; i++) {
    certaintyGrid[i] = [];
    for(j = 0; j < zDim; j++) {
        certaintyGrid[i][j] = [];
        for(k = 0; k < yDim; k++) {
            //start them as non existent (no certainty) or else it could case errors
            var material = new THREE.MeshLambertMaterial({color: 0x0000ff, transparent: false, opacity: 0 });
            var cube = new THREE.Mesh(geometry, material);

            cube.position.set(i,k,j);

            material.certaintyValue = 0;


            //this is just for testing - creates a wall of squares along the edges of the grid
            if(j == 0 || i == 0 || k == 0 || j == zDim - 1 || k == yDim - 1 || i == xDim -1) {
                material.certaintyValue = Math.floor(Math.random() * 220);
            }


            //keep our pointer to our object so we can add it later if it gets any certainty
            certaintyGrid[i][j][k] = cube;
        }
    }
}


/* Attempt to merge the meshes
var geo = new THREE.Geometry();
var meshTest = new THREE.Mesh(geo, material);
for (i = 0; i < xDim; i++) {
    for(j = 0; j < zDim; j++) {
        for(k = 0; k < yDim; k++) {
            THREE.GeometryUtils.merge(geo, certaintyGrid[i][j][k]);
        }
    }
}
scene.add(meshTest);
*/


//this is where it loops and updates the camera and scene
var render = function () {
    requestAnimationFrame(render);

    //testin stuff
    drone.rotation.y += 0.01;


    //makes it so the light is always comming from behind where the drone is facing
    directionalLight.position.x = Math.sin(drone.rotation.y);
    directionalLight.position.z = Math.cos(drone.rotation.y);


    //update the cubes based on their certainty values (maybe make this "smarter" later so it only updates the ones changed)
    for(i = 0; i < xDim; i++) {
        for(j = 0; j < zDim; j++) {
            for(k = 0; k < yDim; k++) {
                var currMater = certaintyGrid[i][j][k].material;
                if(currMater.certaintyValue > objectThreshhold) {
                    if(currMater.opacity != 1) {
                        if (currMater.transparent == false)
                            scene.add(certaintyGrid[i][j][k]);
                        currMater.transparent = false;
                        currMater.opacity = 1;
                    }
                    var red = (currMater.certaintyValue - objectThreshhold)/255;
                    var blue = (255 - (currMater.certaintyValue - objectThreshhold))/255;
                    currMater.color.setRGB(red, .2, blue);
                } else if (currMater.certaintyValue < 1) {
                    if(currMater.opacity != 0) {
                        currMater.transparent = false;
                        currMater.opacity = 0;
                        scene.remove(certaintyGrid[i][j][k]);
                    }
                } else {
                    if(currMater.opacity == 0 || currMater.opacity == 1) {
                        currMater.color.setHex(0x0000ff);
                        currMater.transparent = true;
                        if(currMater.opacity == 0) //only add it if we are going from no certainty
                            scene.add(certaintyGrid[i][j][k]);
                    }
                    currMater.opacity = 0.05 * (currMater.certaintyValue + 1);
                }
            }
        }
    }

    //update the views and cameras
    for ( var ii = 0; ii < views.length; ++ii ) {
        view = views[ii];
        camera = view.camera;

        view.updateCamera(camera, scene);
        renderer.setScissor( view.left, view.bottom, view.width, view.height );
        renderer.setViewport( view.left, view.bottom, view.width, view.height );
        //renderer.setClearColor( view.background );

        camera.aspect = view.width / view.height;
        camera.updateProjectionMatrix();

        renderer.render( scene, camera );
    }
};


//now actually get us started (puts us in the infinite run loop)
render();

另外,我在这个简单的 HTML 文件中运行它只是为了测试:

Also, I have it running in this simple HTML file just for testing :

<html>
<head>
<title>VFF Vizualization Test</title>
</head>

<body>
<canvas id="VFFCanvas" width="640" height="480"></canvas>
<script src="three.min.js"></script>
<script src="VFF.js"></script>
</body>
</html>

我进行了一些搜索并尝试了几种不同的方法来加速它,包括使用更少的材料,但它对运行速度没有太大帮助(如果有助于提高性能,可以很容易地使用几种不同的颜色,并且不就颜色而言,不需要大量细节).此外,我尝试合并立方体的网格,它极大地加快了速度,但随后所有立方体只有一种材质无法实现这一点.我在 MeshFaceMaterial 上看到了一些东西,并认为它可能有用,但只看到它在单个未合并的网格(通常是立方体)上实现,并且不确定它是否可以用于此应用程序或如何分配材料.

I did some searching and tried a few different methods to speed it up, including using fewer materials, but it did not help running speed much if at all (can easily get by with a few different colors if it helps performance and don't need large level of detail as far as colors go). Also, I tried merging the meshes of the cubes and it speed it up fantastically, but then there was only one material for all the cubes which won't work for this. I saw some things on MeshFaceMaterial and thought it might work, but only saw it implemented on single, unmerged meshes (usually cubes) and wasn't sure if it could be used for this application or how it works assigning the materials.

我愿意接受任何可行的想法(不仅仅是 MeshFaceMaterial)并感谢您的帮助!

I'm open to any ideas that could work (not just MeshFaceMaterial) and appreciate the help!

推荐答案

我发现了一个特殊的性能杀手:您通过在 render() 函数中添加和删除对象来修改场景.相反,您应该创建您需要的所有对象,并将不需要的对象设为不可见或可见.使用可见性而不是透明度使事物不可见.

I spot one particular performance killer: you're modifying the scene by adding and removing objects in the render() function. Instead you should create all the objects you need and turn the objects you don't need invisible or visible. Use visibility rather than transparency to make things invisible.

另一个问题(与性能无关)是您正在移动定向光……定向光没有位置,但确实有方向.

Another problem (not performance related) is that you're moving a directional light ... a directional light doesn't have a position, but it does have an orientation.

最后,正如您在代码中所说,您应该真正优化该循环,并且只更改您需要的内容.由于您所做的任何几何更改都必须在每帧上传送到 GPU,这是昂贵的,因此您真的不想进行不需要进行的更改,真正让您的 render() 循环尽可能快.

Lastly, as you say in your code, you should really optimise that loop and only change what you need to. As any geometry changes you make have to be delivered to the GPU on each frame which is expensive, you really don't want to make changes you don't need to make, really keep your render() loop as quick as possible.

我可能还会为此做出的一个特殊更改是添加一种按颜色构建材质的方法.如果你已经有了那个颜色,它会返回那个颜色,否则它会创建那个颜色.可能帮不上什么忙,但一点一滴都能帮上忙.

One particular change I'd probably also make for this is to add a method which builds a material by colour. If you already have that colour it returns that, otherwise it creates that colour. It might not help much, but every little bit can help.

这篇关于Three.js 合并网格但保留单独的材料的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 00:13