WebGL パイプ ネットワークの表示 (および TubeGeometry の最適化)

序文

3D シーンでは配管表示が一般的です。たとえば、地下パイプ ネットワーク、建物内の果物、暖房および換気パイプなどの表示。

[外部リンクの画像転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-VIaL8EZK-1659517949929)(https://p6-juejin.byteimg.com/ tos-cn-i-k3u1fbpfcp /832b7acc7a8d4b8a8e4a8e2f4b3fdb90~tplv-k3u1fbpfcp-watermark.image?)]
パイプラインを確立するには主に 2 つの方法があります。

  • 3DMax C4D Blender などのモデリング ツールによるモデリング。
  • パス データから、プログラムは 3 次元のパイプラインを生成します。

データを介してパイプラインを動的に生成する必要がある場合は、2 番目の方法のみを使用してパイプラインを生成できます。

パイプラインの生成方法

THREE では、パイプラインは TubeGeometry を介して生成できます。
TubeGeometry は、3D スプラインに沿ってチューブを押し出すことができます。パスは、頂点を指定することで定義できます。TubeGeometry パイプ ジオメトリを作成するときに、次のパラメータを入力できます。

  • path (必須) この属性は、THREE.SplineCurve オブジェクトを使用して、パイプラインがたどるパスを指定します。
  • セグメント この属性は、パイプラインのセグメント数を指定します. パスが長いほど、より多くのセグメントを指定する必要があります. デフォルト値は 64 です
  • radius この属性は、パイプの半径を指定します。デフォルト値は 1 です。
  • radiusSegments いいえ この属性は、パイプ セクションのセグメント数を指定します。セグメントが多いほど、パイプの外観が滑らかになります。デフォルト値は 8 です。
  • 閉まっている
  • この属性は、パイプが最後に接続されているかどうかを設定するために使用されます。デフォルト値は 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);

効果は次のとおりです。

[外部リンクの画像転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-rVYbiLeQ-1659517949932)(https://p1-juejin.byteimg.com/ tos-cn-i-k3u1fbpfcp /854ff4a89e4d4be8bccd83cc22bff0e5~tplv-k3u1fbpfcp-watermark.image?)]

TubeGeometry の最適化

TubeGeometry は、パスとセグメント数を指定して、パイプのジオメトリ データを作成します。TubeGeometry のセグメントは均等に分散されているため、問題が発生します。パスが非常に長く複雑な場合は、ジオメトリの精度を確保するためにセグメントの数を多くする必要があります。ただし、チューブはほとんどまっすぐで、湾曲したチューブはわずかです。実際、直線パイプラインの場合、パスの開始点と導入点のみを使用してパスを完全に記述することができます.曲線パイプラインの場合のみ、パスを多くの端に分割する必要があり、各セグメントポイントのデータは曲線の説明をより完全にするため。平均セグメンテーション法を使用すると、必然的に多くのセグメントが直線上に配置されるため、リソースが無駄になります.同時に、セグメントの数が多くなり、作成されるジオメトリ データが比較的大きく肥大化します。プログラムの読み込み、描画効率、ビデオ メモリなどに影響します。たとえば、次の一連の点はすべて 2 本の線を形成します。

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);

以下の結果が得られます。

[外部リンクの画像の転送に失敗しました。ソース サイトにリーチング防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-kGAilvdT-1659517949933)(https://p3-juejin.byteimg.com/tos -cn-i-k3u1fbpfcp /4feb79b374774800a96c7220499aea46~tplv-k3u1fbpfcp-watermark.image?)]

元の 2 セグメントの直線は 3 セグメントの直線の効果になりました. 理由はセグメントの数が足りないためです:
セグメントの数を増やします:

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

効果は次のとおりです。

[外部リンクの画像の転送に失敗しました。ソース サイトにリーチング防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-quD8fAg2-1659517949934)(https://p1-juejin.byteimg.com/tos -cn-i-k3u1fbpfcp /9c9a5d93026a4f5ca8f7e0f34243b4b9~tplv-k3u1fbpfcp-watermark.image?)]

2 本の直線からなるパスを描くには 256 のセグメント数が必要であり、頂点の数は想像できます。

これにより、最適化のアイデアを得ることができます.パスの直線部分については、それをセグメント化する必要はなく、始点と終点に移動するだけです.セグメントの数は、曲線部分にのみ割り当てられます. このようなセグメント化方法は、セグメント数の最も合理的な適用を行うことができます。これに基づいて、新しいクラス PathTubeGeometry を変換しました。
変更されたクラスは、次のように、スライスの数なし (スライスの数は 1) で上記の 2 つの直線を描画します。

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

[外部リンクの画像転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-ZNviGHIS-1659517949934)(https://p1-juejin.byteimg.com/ tos-cn-i-k3u1fbpfcp /4d5b8450ff66429db5df81551544d787~tplv-k3u1fbpfcp-watermark.image?)]

リソースを大幅に節約します。

コーナーエルボー

2 本の直線が接続されている場所に、少し丸みを帯びたコーナー効果を追加して、パイプラインの美学を向上させることができます. 次のコードは、既存のパスに基づいて曲がったパイプを持つパスを自動的に生成できます.

 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 Biaoshu」をフォローして、著者の WeChat を追加して、コミュニケーションを取り、より価値のある記事を時間内に受け取ってください。

おすすめ

転載: blog.csdn.net/netcy/article/details/126144580