Three.js - 从另一个网格的特定面/顶点创建新网格

3

我已经苦于一个Three.js问题几天了,但找不到任何解决方法。以下是我的情况:

1)我有一个浮动的网格,由几个三角形面组成。此网格是从加载器返回的几何体创建的,使用getAttribute('position')获取其顶点和面:如何平滑STL加载的BufferGeometry中的网格三角形

enter image description here

2)现在我想要做的是将底部面“投影”在地板上。

enter image description here

3)随后,通过添加这个新面,创建填充两个面之间空间的结果网格。

enter image description here

我已经在第二步遇到麻烦...为了创建一个新面,我应该已经将它的三个顶点添加到geometry.vertices中。我这样做了,克隆原始面的顶点。我使用 geometry.vertices.push() 的结果知道它们的新索引,然后使用那些索引(-1)来最终创建新面。但它的形状很奇怪,位置和大小也不对。我想我没有正确理解世界/场景/向量位置等价性理论 :P

我尝试应用以下方法,但没有成功:

如何在three.js中获取一个顶点的绝对位置? 如何使用投影在Three.js中将世界坐标转换为屏幕坐标 http://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works 我发现如果直接克隆完整的原始面并将其添加到网格中,该面将被添加但处于相同位置,因此我不能更改其顶点以将其放置在地板上(或者至少不修改原始面的顶点!)。我的意思是,我可以更改它们的x、y、z属性,但它们的度量非常小,与原始网格尺寸不匹配。
有人能帮我理解这个概念吗?
编辑:源代码
            // Create geometry
            var geo = new THREE.Geometry();
            var geofaces = [];
            var geovertices = [];

            original_geometry.updateMatrixWorld();

            for(var index in original_geometry.faces){          
                // Get original face vertexNormals to know its 3 vertices
                var face = original_geometry[index];
                var vertexNormals = face.vertexNormals;

                // Create 3 new vertices, add it to the array and then create a new face using the vertices indexes
                var vertexIndexes = [null, null, null];
                for (var i = 0, l = vertexNormals.length; i < l; i++) {
                    var vectorClone = vertexNormals[i].clone();
                    vectorClone.applyMatrix4( original_geometry.matrixWorld );
                    //vectorClone.unproject(camera); // JUST TESTING
                    //vectorClone.normalize(); // JUST TESTING

                    var vector = new THREE.Vector3(vectorClone.x, vectorClone.z, vectorClone.y)
                    //vector.normalize(); // JUST TESTING
                    //vector.project(camera); // JUST TESTING
                    //vector.unproject(camera); // JUST TESTING
                    vertexIndexes[i] = geovertices.push( vector ) - 1;
                }
                var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
                geofaces.push(newFace);
            }

            // Assign filled arrays to the geometry
            geo.faces = geofaces;
            geo.vertices = geovertices;

            geo.mergeVertices();
            geo.computeVertexNormals();
            geo.computeFaceNormals();

            // Create a new mesh with resulting geometry and add it to scene (in this case, to the original mesh to keep the positions)
            new_mesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial(material) ); // material is defined elsewhere
            new_mesh.position.set(0, -100, 0);
            original_mesh.add( new_mesh );
2个回答

1
我创建了一个完全可操作的JSFiddle,以尝试并更清楚地查看问题。使用这个STL(比我的本地示例小),我甚至看不到添加到场景中的糟糕克隆面。也许它们太小或者不在焦点内。
请查看calculateProjectedMesh()函数,在这里我尝试克隆并放置底部面(已检测到,因为它们具有不同的materialIndex):
JSFiddle链接:https://jsfiddle.net/tc39sgo1/
var container;
var stlPath = 'https://dl.dropboxusercontent.com/s/p1xp4lhy4wxmf19/Handle_Tab_floating.STL';

var camera, controls, scene, renderer, model;

var mouseX = 0,
    mouseY = 0;

var test = true;
var meshPlane = null, meshStl = null, meshCube = null, meshHang = null;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

/*THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;*/

var materials = [];
materials.push( new THREE.MeshPhongMaterial({color : 0x00FF00, side:0, shading: THREE.FlatShading, transparent: true, opacity: 0.9, overdraw : true, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0xFF0000, transparent: true, opacity: 0.8, side:0, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0x0000FF, side:2, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.05 });

init();
animate();

function webglAvailable() {
    try {
        var canvas = document.createElement('canvas');
        return !!(window.WebGLRenderingContext && (
        canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
    } catch (e) {
        return false;
    }
}

function init() {
    container = document.createElement('div');
    document.body.appendChild(container);

    camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100000000);
    camera.position.x = 1500;
    camera.position.z = -2000;
    camera.position.y = 1000;

    controls = new THREE.OrbitControls(camera);

    // scene
    scene = new THREE.Scene();

    var ambient = new THREE.AmbientLight(0x101030); //0x101030
    scene.add(ambient);

    var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
    directionalLight.position.set(0, 3, 0).normalize();
    scene.add(directionalLight);

    var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
    directionalLight.position.set(0, 1, -2).normalize();
    scene.add(directionalLight);

        if (webglAvailable()) {
        renderer = new THREE.WebGLRenderer();
    } else {
        renderer = new THREE.CanvasRenderer();
    }
        renderer.setClearColor( 0xCDCDCD, 1 );

    // renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);

    document.addEventListener('mousemove', onDocumentMouseMove, false);
    window.addEventListener('resize', onWindowResize, false);

        createPlane(500, 500);
        createCube(500);
        loadStl();
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function onDocumentMouseMove(event) {
    mouseX = (event.clientX - windowHalfX) / 2;
    mouseY = (event.clientY - windowHalfY) / 2;
}

function animate() {
    requestAnimationFrame(animate);
    render();
}

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

function createPlane(width, height) {
        var planegeometry = new THREE.PlaneBufferGeometry(width, height, 0, 0);
        var material = new THREE.MeshLambertMaterial({
            color: 0xFFFFFF,
            side: THREE.DoubleSide
        });
        planegeometry.computeBoundingBox();
        planegeometry.center();

        meshPlane = new THREE.Mesh(planegeometry, material);
        meshPlane.rotation.x = 90 * (Math.PI/180);
        //meshPlane.position.y = -height/2;
        scene.add(meshPlane);
}

function createCube(size) {
    var geometry = new THREE.BoxGeometry( size, size, size );                       
        geometry.computeFaceNormals();
        geometry.mergeVertices();
        geometry.computeVertexNormals();
        geometry.center();

    var material = new THREE.MeshPhongMaterial({
              color: 0xFF0000,
                opacity: 0.04,
                transparent: true,
                wireframe: true,
                side: THREE.DoubleSide
        });
        meshCube = new THREE.Mesh(geometry, material);
        meshCube.position.y = size/2;
        scene.add(meshCube);
}

function loadStl() {        
        var loader = new THREE.STLLoader();             
        loader.load( stlPath, function ( geometry ) {   
                        // Convert BufferGeometry to Geometry
                        var geometry = new THREE.Geometry().fromBufferGeometry( geometry );

                        geometry.computeBoundingBox();
                        geometry.computeVertexNormals();
                        geometry.center();

                        var faces = geometry.faces;
                        for(var index in faces){
                                var face = faces[index];
                                var faceNormal = face.normal;
                                var axis = new THREE.Vector3(0,-1,0);
                                var angle = Math.acos(axis.dot(faceNormal));
                                var angleReal = (angle / (Math.PI/180));
                                if(angleReal <= 70){
                                    face.materialIndex = 1;
                                }
                                else{
                                    face.materialIndex = 0;
                                }
                        }

                geometry.computeFaceNormals();
                        geometry.computeVertexNormals();

                    meshStl = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
                        meshStl.position.x = 0;
                        meshStl.position.y = 400;
            scene.add( meshStl );

                        // Once loaded, calculate projections mesh
                        calculateProjectedMesh();
        });
}

function calculateProjectedMesh(){
            var geometry = meshStl.geometry;
            var faces = geometry.faces;
            var vertices = geometry.vertices;

            var geometry_projected = new THREE.Geometry();
            var faces_projected = [];
            var vertices_projected = [];

            meshStl.updateMatrixWorld();

            for(var index in faces){
                    var face = faces[index];

                    // This are the faces
                    if(face.materialIndex == 1){

                            var vertexIndexes = [face.a, face.b, face.c];
                            for (var i = 0, l = vertexIndexes.length; i < l; i++) {
                                    var relatedVertice = vertices[ vertexIndexes[i] ];
                                    var vectorClone = relatedVertice.clone();
                                    console.warn(vectorClone);
                                    vectorClone.applyMatrix4( meshStl.matrixWorld );

                                    ////////////////////////////////////////////////////////////////
                                    // TEST: draw line
                                    var geometry = new THREE.Geometry();
                                    geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
                                    //geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
                                    geometry.vertices.push(new THREE.Vector3(vectorClone.x, meshPlane.position.y, vectorClone.z));
                                    var line = new THREE.Line(geometry, lineMaterial);
                                    scene.add(line);
                                    console.log("line added");
                                    ////////////////////////////////////////////////////////////////    

                                    vectorClone.y = 0;
                                    var vector = new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z);
                                    vertexIndexes[i] = vertices_projected.push( vector ) - 1;
                            }
                            var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
                            newFace.materialIndex = 2;
                            faces_projected.push(newFace);
                    }
            }
            geometry_projected.faces = faces_projected;
            geometry_projected.vertices = vertices_projected;
            geometry_projected.mergeVertices();
            console.info(geometry_projected);

            meshHang = new THREE.Mesh(geometry_projected, new THREE.MeshFaceMaterial(materials));
            var newY = -(2 * meshStl.position.y) + 0;
            var newY = -meshStl.position.y;
            meshHang.position.set(0, newY, 0);
            meshStl.add( meshHang );        
}
编辑: 终于!我搞定了!为了克隆原始的面,我必须访问它们的3个原始顶点,使用"a"、"b"和"c"属性引用原始几何体的"vertices"数组中的Vector3实例的索引。

我将3个顶点克隆并将其Z位置压平为零,使用它们的新索引创建新的面并将其添加到投影网格(蓝色)中。

我还添加了线作为两个面之间的视觉联合。现在我已经准备好进行第三步了,但我认为这已经足够复杂来结束这个问题。

感谢updateMatrixWorld提示!这对于实现我的目标至关重要 ;)


0

试一下这个

 original_geometry.updateMatrixWorld();
            var vertexIndexes = [null, null, null];
            for (var i = 0, l = vertexNormals.length; i < l; i++) {
              var position = original_geometry.geometry.vertices[i].clone();
              position.applyMatrix4( original_geometry.matrixWorld );

                var vector = new THREE.Vector3(position.x, position.y, position.z)

                vertexIndexes[i] = geovertices.push( vector ) - 1;
            }

是的,我刚才忽略了我知道哪个是底面的计算部分,但那部分已经完成了,谢谢。我的问题是如何获得面的三个顶点。你是怎么做到的?我使用原始面的 vertexNormals 属性(这是一个包含3个向量的数组,每个向量都有其 x、y、z 属性)。但单位...非常低,例如 -0.2、0.4、2 等等。这些是向量单位,对吗?我的向量/面相加方式正确吗?我首先添加3个克隆向量,获取其索引,然后用它们来添加面。但我看到你只使用 0、1、2 数字作为参数,我感到困惑。 - spacorum
在你的渲染中,执行以下代码:original_geometry.updateMatrixWorld(); - jonsoft
要获取真正的三个顶点,请尝试使用以下代码: vectorClone.applyMatrix4(original_geometry.matrixWorld); - jonsoft
所以我提到的链接之一指向了正确的方向:https://dev59.com/Wmgu5IYBdhLWcg3wMkfF正如您所看到的,我将其应用于源代码。但是每次尝试时都会出现错误:original_geometry.updateMatrixWorld不是一个函数几何体是有效的,并且它有它的顶点、面等。但是该方法不是。这可能与加载器返回的几何体/对象类型有关吗? - spacorum
哇,这是我在谷歌上的噩梦 :)https://www.google.es/search?num=20&safe=off&q=%22updateMatrixWorld+is+not+a+function%22+-%22you+can+replicate+this%22除了一个与此无关的错误报告之外,没有其他提到“updateMatrixWorld不是函数”的结果。现在感到很迷茫。 - spacorum
显示剩余7条评论

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