乐趣区

关于前端:WebGL管网展示及TubeGeometry优化

前言

管路展现在三维场景中很常见。比方公开管网,修建外面的水果,暖通管道等等的展现。

建设管路的形式次要两种:

  • 通过 3DMax C4D Blender 等建模工具进行建模。
  • 通过门路数据,程序生成三维管路。

如果须要动静的通过数据生成管路,只能采纳第二种形式来生成。

生成管路的形式

在 THREE 中,通过 TubeGeometry 能够生成管路。
TubeGeometry 能够沿着一条三维样条曲线拉伸出一根管子。能够通过指定顶点来定义门路。创立 TubeGeometry 管道几何体时可输出以下参数:

  • path(必填)该属性用一个 THREE.SplineCurve 对象来指定管道遵循的门路
  • segments 该属性指定管道的分段数,门路越长指定的段数应该越多,默认值是 64
  • radius 该属性指定管道的半径,默认值是 1
  • radiusSegments 否 该属性指定管道截面的分段数,段数越多管道看上去越润滑,默认值是 8
  • closed
  • 该属性来设置管道是否收尾连贯,默认值 false

比方:

const points = [{
          x: 0,
          y: 0,
          z: 0
        },
        {
          x: 100,
          y: 0,
          z: 0
        },
        {
          x: 100,
          y: 0,
          z: 200
        },
      ];
      
  const path = createPath(points);

  const pipeGeometry = new THREE.TubeGeometry(path, 256, radius, 64);
  const pipeMaterial = new THREE.MeshPhongMaterial({color});
  const pipe = new THREE.Mesh(pipeGeometry, pipeMaterial);

成果如下:

TubeGeometry 的优化

TubeGeometry 通过指定一个 path 门路和分段数来创立管路的几何数据。TubeGeometry 的分段是平均分配,这会导致一个问题,如果门路很长很简单,分段数量须要须要很大能力保障几何体的准确性。然而,管路大部分都是直线,只有大量的蜿蜒的管路。如果一条直线管路,其实只需取门路的起始点和介绍点就能够对门路进行残缺的形容,只有蜿蜒的管路,才须要把门路分成很多端,别离取每一个分段点的数据,能力较好的实现曲线的形容。如果采纳均匀分段的形式,势必有很多分段落在直线的局部,导致资源的节约,同时分段数量要求高,创立的几何数据会比拟宏大臃肿,影响程序的加载、绘制效率、显存等等。比方,以下系列点都组成两条直线:

const points = [{
          x: 0,
          y: 0,
          z: 0
        },
        {
          x: 100,
          y: 0,
          z: 0
        },
        {
          x: 100,
          y: 0,
          z: 200
        },
      ];

分段数是 4 的时候:

new THREE.TubeGeometry(path, 4, radius, 64);

失去的是如下后果:

原本两段直线,变成了 3 段直线的成果,起因就是分片数量不够:
减少分段数:

new THREE.TubeGeometry(path, 256, radius, 64);

成果如下:

为了绘制两条直线组成的门路,须要 256 的分段数量, 顶点数量可想而知。

所以咱们能够有一种优化思路,对于门路的直线局部,不须要分段,只须要去起始点和起点即可,分段数只调配给曲线的局部。这样的分段形式,能够是分段的数量失去最正当的利用。基于此,咱们革新出一个新的类:PathTubeGeometry。
革新后的类,绘制上述两条直线,不须要分片数(分片数量为 1),如下:

new PathTubeGeometry(path, 1, radius, 64);

极大的节约了资源。

拐角弯管

两条直线连贯的中央,能够通过加上一点圆角的成果来减少管路的好看度,通过上面代码能够主动依据已有门路生成带弯管的门路。

 function getRoundCornerPath(oldPath, radius, path = new Path3D()) {
      let points = oldPath.points;
      points = removeDup(points);
      let p0 = points[0];
      path.moveTo(p0);
      for (let i = 1; i < points.length - 1; i++) {let pre = points[i - 1],
          p = points[i],
          next = points[i + 1];
        let sub1 = p.clone().sub(pre).setLength(radius),
          sub2 = next.clone().sub(p).setLength(radius);
        let v1 = p.clone().sub(sub1),
          v2 = p.clone().add(sub2);
        path.lineTo(v1);
        path.curveTo(p.x, p.y, p.z, v2.x, v2.y, v2.z);
      }
      let pl = points[points.length - 1];
      path.lineTo(pl);
      return path;
    }

结语

关注公号“ITMan 彪叔”能够增加作者微信进行交换,及时收到更多有价值的文章。

退出移动版