【问题标题】:Threejs merge with multiple materials applies only one materialThreejs 与多种材质合并只应用一种材质
【发布时间】:2023-03-24 13:45:01
【问题描述】:

我对此进行了搜索,并在stackoverflow上找到了几个示例,但答案并没有解决我的问题。

我尝试过的:

首先,我创建了用于组的几何存储桶和一个数组来存储我的材质。

var totalGeom = new THREE.Geometry();
var materials = [];

然后我使用 for 循环遍历我的数据 (strData) 并调用 addMapMarker3d。

  for(var i=0; i<strData.length;i++){
     addMapMarker3d([strData[i].lat,strData[i].lon], strData[i].time, measureColors[i]);
  }

addMapMarker3d函数如下:

addMapMarker3d = function(latLng, height, color){
  var max = 600;
  var dist = 25;
  var opacity = (height/max);

  var geometry = new THREE.BoxGeometry( Math.floor(dist), height, Math.floor(dist));

  //create the material and add it to the materials array
  var material = new THREE.MeshBasicMaterial({ transparent:true, color: Number(color), opacity:opacity});
  this.materials.push(material);

  //create a mesh from the geometry and material.
  var cube = new THREE.Mesh( geometry, material);

  //leaf is a simple lat/lng to pixel position converter
  var actualMarkerPos = leaf.getPoint(latLng);
  //apply the position on a 5000x5000 cartesian plane
  var extent = 5000;
  cube.position.setZ((actualMarkerPos[1] * extent) - extent/2);
  cube.position.setX(-((actualMarkerPos[0] * extent) - extent/2));
  cube.position.setY(height/2);

  //update the matrix and add the cube to the totalGeom bucket
  cube.updateMatrix();
  totalGeom.merge( cube.geometry, cube.matrix);
}

在 for 循环运行并创建所有立方体之后:

  var mats = new THREE.MeshFaceMaterial(materials)
  var total = new THREE.Mesh(totalGeom, mats);

  world.scene.add(total);

问题

虽然几何合并功能,并且我的视口以大大提高的 FPS 运行,但所有立方体都具有完全相同的颜色和不透明度。看来合并使用的是我提供的 10k 的单一材料。 有什么方法可以确保几何图形使用数组中提供的材料?我做错了什么吗?

如果我在 addMapMarker3d 中尝试这个:

totalGeom.merge( cube.geometry, cube.matrix, materials.length-1);

我得到“Uncaught TypeError: Cannot read property 'transparent' of undefined”并且没有渲染,我不明白,因为通过示例,每个几何图形都应该索引到材料数组中的材料。

three.js r.70

【问题讨论】:

    标签: three.js


    【解决方案1】:

    您看到性能提高的唯一原因是,在合并之后,只有一种材料。如果您希望场景具有多种材质,则不应合并。

    加入同组:

    var group = new THREE.Object3D();
    group.add (object1);
    group.add (object2);
    group.add (object3);
    

    【讨论】:

    • 好消息是,我可以在没有 FPS 改进的情况下生活。我想将该组视为单个网格对象。材料没有被应用是否有原因?
    • 正如我所说,当你合并时,你只会得到一种材料。但是您可以将所有这些对象都视为一个对象(关于转换),方法是将它们全部添加到同一个组中。
    • 啊,我是按照这个答案来的,很明显,我看不懂:stackoverflow.com/questions/27217388/…
    • 我知道您为我提供了一个更好的途径来将网格视为一个单独的组。但是,当我们确实使用合并时,如果只使用一个,为什么要以数组的形式提供材料呢?如果只使用一种材料,为什么合并有一个偏移索引参数?谢谢!
    【解决方案2】:

    以下技术仅使用一种材质,但允许您保留每个合并对象的单独颜色。我不知道是否可以保留每个合并对象的单独 alpha。

    http://jsfiddle.net/looshi/nsknn53p/61/

    对于每个网格,设置其每个 geometry.faces 颜色:

    function makeCube(size, color) {
        var geom = new THREE.BoxGeometry(size, size, size);
    
        for (var i = 0; i < geom.faces.length; i++) {
            face = geom.faces[i];
            face.color.setHex(color);
        }
        var cube = new THREE.Mesh(geom);
        return cube;
    }
    

    然后,在您要网格化的父几何体中,设置其材质 vertexColors 属性。

    var parentGeometry = new THREE.Geometry();
    var parentMatrial = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        shading: THREE.SmoothShading,
        vertexColors: THREE.VertexColors
    });
    
    
       // in a loop you could create many objects and merge them
    for (var i = 0; i < 1000; i++) {
          cube = makeCube(size, color);
          cube.position.set(x, y, z);
          cube.rotation.set(rotation,rotation,rotation);
          cube.updateMatrix()
          parentGeometry.merge(cube.geometry, cube.matrix);
    }
    // after you're done creating objects and merging them, add the parent to the scene 
    parentMesh = new THREE.Mesh(parentGeometry, parentMatrial);
    scene.add(parentMesh);
    

    【讨论】:

      【解决方案3】:

      我最初的问题没有得到解答:有什么方法可以确保几何图形使用数组中提供的材料?

      答案是肯定的。 在合并期间可以将多种材质应用于单个网格。将材质推送到材质数组后,合并将利用几何面材质索引。当创建总网格时,将数组提供给 new THREE.MeshFaceMaterial([materialsArray]) 时,合并将应用多种材质。这解决了语法的奥秘。仅仅因为您提供了一组材料并不意味着合并将以迭代方式使用每种材料,因为从 r71 开始合并对象。面必须通知合并使用材质数组中的哪种材质。

      我将它用于未渲染的场景,并导出最终的 obj。如果您需要渲染性能,请参阅其他答案之一以了解某些选项。

      几何上的面数组上的一个简单的 for 循环会通知合并应用哪种材料:

        addMapMarker3d = function(latLng, height, color){
        var max = 600;
        var dist = 25;
        var opacity = (height/max);
      
        var geometry = new THREE.BoxGeometry( Math.floor(dist), height, Math.floor(dist));
      
        //create the material and add it to the materials array
        var material = new THREE.MeshBasicMaterial({ transparent:true, color: Number(color), opacity:opacity});
        this.materials.push(material);
      
        //set the material index of each face so a merge knows which material to apply
        for ( var i = 0; i < geometry.faces.length; i ++ ) {
          geometry.faces[i].materialIndex = this.materials.length-1;
        }
      
        //create a mesh from the geometry and material.
        var cube = new THREE.Mesh( geometry, material);
      
        //leaf is a simple lat/lng to pixel position converter
        var actualMarkerPos = leaf.getPoint(latLng);
        //apply the position on a 5000x5000 cartesian plane
        var extent = 5000;
        cube.position.setZ((actualMarkerPos[1] * extent) - extent/2);
        cube.position.setX(-((actualMarkerPos[0] * extent) - extent/2));
        cube.position.setY(height/2);
      
        //update the matrix and add the cube to the totalGeom bucket
        cube.updateMatrix();
        totalGeom.merge( cube.geometry, cube.matrix);
      }
      

      【讨论】:

      • 性能如何?我尝试了类似的方法,但是对于大约 1000 个对象,使用这种技术的 FPS 非常慢,但我会再试一次。在我的情况下,最好像你一样保留个别材料。
      • 如果是几个几何图形,没关系。例如。在闪亮的花盆中定义植物:植物的哑光透明 PNG 和闪亮的花盆,然后随意克隆合并的网格。我的示例使用 360k 立方体。但是只渲染一帧以供检查和导出网格,因此是单个网格。我不需要相机变换。在立方体的每个面上添加一种材料意味着矩阵变换的传递次数是 6 倍。您可以想象,虽然 100k 立方体将花费 600k 材料计算。网格合并有它的位置,但要明智。正如您和@gaitat 提到的,运行时性能有多种选择。
      • @looshi ,忘记@你了。请看我的回复。
      猜你喜欢
      • 2015-01-16
      • 1970-01-01
      • 2023-03-26
      • 2015-03-04
      • 2018-02-28
      • 2013-08-07
      • 2018-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多