web-dev-qa-db-ja.com

幅と太さのあるThree.js3Dラインシリーズを作成するにはどうすればよいですか?

幅と太さのあるThree.js3Dラインシリーズを作成する方法はありますか?

Three.jsラインオブジェクトは線幅をサポートしていますが、この属性はWebGLのすべてのプラットフォームのすべてのブラウザーでまだサポートされていません。

Three.jsで線幅を設定する場所は次のとおりです。

    var material = new THREE.LineBasicMaterial({
        color: 0xff0000,
        linewidth: 5
    });

幅のあるThree.jsリボンオブジェクトが最近削除されました。

Three.jsチューブオブジェクトは3D押し出しを生成しますが、ベジェベースであるため、線はコントロールポイントを通過しません。

Three.jsで、幅、太さ、半径など、ユーザーが定義できる「バルク」のある線のシリーズ(ポリライン、プロットライン)を描画する方法を考えられる人はいますか?

この質問は、この質問の言い換えかもしれません: three.jsでグラフを押し出す

すぐに利用できる方法はないと思いますので、この質問に答える簡単な関数を作成する努力に参加したいと思います。

しかし、既存の実行可能な方法を指す応答はクールでしょう...

WestLangleyが示唆しているように、考えられる解決策の1つには、一定のピクセル幅のポリラインが含まれます。これは、Three.jsキャンバスレンダラーで現在利用可能です。

2つのレンダラーの比較を次に示します。

GitHub Pagesを介して比較されたCanvasとWebGLの行

jsFiddleを介して比較されたCanvasおよびWebGL行

enter image description here

線幅を指定でき、両方のレンダラーで同様の結果が発生するソリューションは非常に優れています。

ただし、線に実際の物理的構造がある3D線の考え方は他にもあります。彼らは影を落とし、イベントに反応します。これらも調べる必要があります。

複数のメッシュで構成されたラインの2つのデモを含むGitHubPagesへのリンクは次のとおりです。

Spheres and Cubes Polyline

球と円柱のポリライン

'高価なソリューション。各ジョイントは完全な球で構成されています。

Cubes Polylines

キューブポリライン

私の推測では、これらのいずれかを滑らかな単一メッシュとして構築することは、解決すべき問題にとって複雑になるでしょう。それまでの間、幅が広く高さのある3Dラインの部分的な視覚化へのリンクがあります。

jsFiddleの3Dボックスライン

3d box lines

目標は、「ダミーのために、低レベルの複雑さで、つまり、ダミーのためにコーディングする必要がある」ことです。したがって、3D線は、球や立方体を追加するのと同じくらい簡単でなじみ深いものでなければなりません。ジオメトリ+マテリアル=メッシュ>シーン。また、ジオメトリは、頂点と面を作成するという点で非常に経済的である必要があります。

線には幅と高さが必要です。上は常にY方向です。デモはこれを示しています。デモに表示されていないのは、コーナーがうまくマイター接続されていることです...

12
Theo

私はあなたの要件のほとんどを満たすと私が信じる可能な解決策を作り上げました:

http://codepen.io/garciahurtado/pen/AGEsf?editors=001

enter image description here

概念は非常に単純です。「ワイヤーフレームモード」で任意のジオメトリをレンダリングしてから、フルスクリーンGLSLシェーダーを適用して、ワイヤーフレームラインに太さを追加します。

シェーダーは、ThreeJSディストリビューションのブラーシェーダーに触発されています。このシェーダーは、基本的に、水平軸と垂直軸に沿って画像を何回もコピーします。そのプロセスを自動化し、コピー数をユーザー定義のパラメーターにし、コピーが1ピクセルオフセットされるようにしました。

デモでは(オルソカメラを使用して)3Dキューブメッシュを使用しましたが、ポリラインに変換するのは簡単なはずです。

このことの本当の肉とジャガイモは、カスタムシェーダー(フラグメントシェーダー部分)にあります:

    uniform sampler2D tDiffuse;
    uniform int edgeWidth;
    uniform int diagOffset;
    uniform float totalWidth;
    uniform float totalHeight;
    const int MAX_LINE_WIDTH = 30; // Needed due to weird limitations in GLSL around for loops
    varying vec2 vUv;

    void main() {
        int offset = int( floor(float(edgeWidth) / float(2) + 0.5) );
        vec4 color = vec4( 0.0, 0.0, 0.0, 0.0);

        // Horizontal copies of the wireframe first
        for (int i = 0; i < MAX_LINE_WIDTH; i++) {
            float uvFactor = (float(1) / totalWidth);
            float newUvX = vUv.x + float(i - offset) * uvFactor;
            float newUvY = vUv.y + (float(i - offset) * float(diagOffset) ) * uvFactor;  // only modifies vUv.y if diagOffset > 0
            color = max(color, texture2D( tDiffuse, vec2( newUvX,  newUvY  ) ));    
            // GLSL does not allow loop comparisons against dynamic variables. Workaround below
            if(i == edgeWidth) break; 
        }

        // Now we create the vertical copies
        for (int i = 0; i < MAX_LINE_WIDTH; i++) {
            float uvFactor = (float(1) / totalHeight);
            float newUvX = vUv.x + (float(i - offset) * float(-diagOffset) ) * uvFactor; // only modifies vUv.x if diagOffset > 0
            float newUvY = vUv.y + float(i - offset) * uvFactor;
            color = max(color, texture2D( tDiffuse, vec2( newUvX, newUvY ) ));  
            if(i == edgeWidth) break;
        }

        gl_FragColor = color;
    }

長所:

  • 線の頂点以外に追加のジオメトリは必要ありません
  • 線の太さはユーザーが定義できます
  • フルスクリーンシェーダーはGPU上で比較的穏やかである必要があります
  • WebGLキャンバス内に完全に実装できます

短所:

  • 線の太さは、水平エッジと垂直エッジではピクセルパーフェクトに近くなりますが、対角エッジではわずかにずれます。これは使用されているアルゴリズムによるものであり、ソリューションの制限です。とはいえ、線の太さが薄く複雑な形状の場合、これは肉眼ではほとんど目立ちません。
  • 線の間の接合部は、十分に大きい線の太さのギャップを示します。 Codepenデモで遊んで、私が何を意味するかを確認できます。 2番目の「対角パス」を追加することでこれに対する解決策を実装し始めましたが、少し毛むくじゃらになり、これはより厚い線の太さ(+8ピクセル)または極端な線の角度でのみ問題になると思います。このソリューションに興味がある場合は、元のソースを見て、私がどこに向かっていたかを確認できます。
  • これはフルスクリーンフィルターを使用するため、この厚さのオブジェクトを表示するためにWebGLコンテキストのみを使用できます。さまざまな線幅を表示するには、追加のレンダリングパスが必要になります。
7
Garcia Hurtado

潜在的な解決策として。 3Dポイントを取得し、THREE.Vector3.projectメソッドを使用して画面空間の座標を計算することができます。次に、canvasを使用するだけで、lineToおよびmoveTo操作になります。 Canvas 2dコンテキストは、可変の線の太さをサポートします。

var w = renderer.domElement.innerWidth;
var h = renderer.domElement.innerHeight;
vector.project(camera);
context2d.lineWidth = 3;
var x = (vector.x+1)*(w/2);
var y = h - (vector.y+1)*(h/2);
context2d.lineTo(x,y);

また、同じキャンバスを使用できるとは思わないので、glレンダリングコンテキストキャンバスの上のレイヤー(別のキャンバス)である必要があります。

カメラの変更頻度が低い場合は、ポリゴンからラインを作成し、カメラの変換に基づいて頂点の位置を更新することもできます。正投影カメラの場合、回転のみが頂点位置の操作を必要とするため、これが最適に機能します。

最後に、キャンバスのクリアを無効にして、円またはボックス内にオフセットを付けて線を数回描画することができます。その後、クリアを再度有効にすることができます。これにはいくつかの追加の描画操作が必要になりますが、おそらく最もスケーラブルなアプローチです。

箱から出して期待どおりに行が機能しない理由は、ANGLEがどのように機能するかによるものです。これは、Chromeで使用され、FirefoxではDirectXを介してOpenGLをエミュレートします。Guys ANGLEによると、WebGL仕様では最大1の線の太さしかサポートされていないため、バグとは見なされず、「修正」するつもりはありません。ただし、線の太さはWindows以外のOSでも機能するはずです。使用されません。

2
travnik