three.js を使って立体的な矢印線を描く詳細な手順

three.js を使って立体的な矢印線を描く詳細な手順

需要: この需要は緊急に必要です!地下鉄のシーンでは、脱出経路を示す矢印を描かなければなりません。この矢印を描くのに十数時間かかり、ようやく完成しましたが、まだ問題がありました。この矢印に対する私の要件は、シーンを拡大または縮小しても、矢印が大きすぎたり小さすぎたりしてはっきりと見えないこと、また形状が変化しないことです。そうしないと、矢印に見えなくなります。

three.jsのLine2.jsとオープンソースライブラリMeshLine.jsを使用します

コードの一部:

描画パス:

/**
 * ルートを描く */

'../build/three.module.js' から THREE として * をインポートします。
'../js.my/MeshLine.js' から { MeshLine、MeshLineMaterial、MeshLineRaycast } をインポートします。

'../js/lines/Line2.js' から { Line2 } をインポートします。
'../js/lines/LineMaterial.js' から LineMaterial をインポートします。
'../js/lines/LineGeometry.js' から LineGeometry をインポートします。
'../js/utils/GeometryUtils.js' から GeometryUtils をインポートします。

'../js.my/CanvasDraw.js' から CanvasDraw をインポートします。

'../js.my/Utils.js' から Utils をインポートします。
'../js.my/Msg.js' から { Msg } をインポートします。

DrawPath = 関数(){

    _self = this とします。

    _canvasDraw = 新しい CanvasDraw() を作成します。
    utils = new Utils(); とします。
    msg = new Msg(); とします。

    this._isDrawing = false;
    this._path = [];
    this._lines = [];
    this._arrows = [];

    _depthTest を true にします。
    _side = 0 とします。

    ビューアコンテナIdを'#cc'とします。
    ビューアコンテナを$(ビューアコンテナId)[0]とします。

    オブジェクトをletします。
    カメラを回す
    回す;
    シーンを演出する

    this.config = 関数 (オブジェクト、カメラ、シーン、ターン) {
        オブジェクト = オブジェクト_;
        カメラ = カメラ_;
        ターン = turn_;
        シーン = scene_;

        this._oldDistance = 1;
        this._oldCameraPos = { x: カメラの位置 x、 y: カメラの位置 y、 z: カメラの位置 z }
    }

    this.start = 関数(){
        if (!this._isDrawing) {
            this._isDrawing = true;
            viewerContainer.addEventListener('click', ray);
            viewerContainer.addEventListener('mousedown', マウスダウン);
            ビューアコンテナにイベントリスナーを追加します('mouseup', mouseup);
        }
        msg.show("線を描くには地面をクリックしてください");
    }

    this.stop = 関数 () {
        if (this._isDrawing) {
            this._isDrawing = false;
            viewerContainer.removeEventListener('click', ray);
            viewerContainer.addEventListener('mousedown', マウスダウン);
            ビューアコンテナにイベントリスナーを追加します('mouseup', mouseup);
        }
        msg.show("線の描画を停止");
    }

    関数 mousedown(パラメータ) {
        this._mousedownPosition = { x: camera.position.x、 y: camera.position.y、 z: camera.position.z }
    }

    関数 mouseup(パラメータ) {
        this._mouseupPosition = { x: camera.position.x、 y: camera.position.y、 z: camera.position.z }
    }

    関数 ray(e) {
        フォーカスボタンを消します。

        raycaster = createRaycaster(e.clientX, e.clientY); とします。
        intersects = raycaster.intersectObjects(objects.all); とします。
        交差する長さが0より大きい場合
            point = intersects[0].pointとします。

            距離を utils.distance(this._mousedownPosition.x、this._mousedownPosition.y、this._mousedownPosition.z、this._mouseupPosition.x、this._mouseupPosition.y、this._mouseupPosition.z) とします。

            (距離<5)の場合{
                _self._path.push({ x: point.x, y: point.y + 50, z: point.z });

                _self._path.length > 1 の場合 {
                    point1 = _self._path[_self._path.length - 2]とします。
                    point2 = _self._path[_self._path.length - 1]とします。

                    線を描画します(ポイント1、ポイント2)。
                    矢印を描画します(ポイント1、ポイント2)。
                }
            }
        }
    }

    関数createRaycaster(clientX, clientY) {
        x = (clientX / $(viewerContainerId).width()) * 2 - 1 とします。
        y = -(clientY / $(viewerContainerId).height()) * 2 + 1 とします。

        standardVector = new THREE.Vector3(x, y, 0.5); とします。

        worldVector = standardVector.unproject(カメラ) とします。

        ray = worldVector.sub(camera.position).normalize() とします。

        raycaster = new THREE.Raycaster(camera.position, ray); を作成します。

        レイキャスターを返します。
    }

    this.refresh = 関数 () {
        _self._path.length > 1 の場合 {
            距離を utils.distance(this._oldCameraPos.x、this._oldCameraPos.y、this._oldCameraPos.z、camera.position.x、camera.position.y、camera.position.z) とします。
            比率を1にします。
            this._oldDistance が 0 の場合
                比率 = Math.abs((this._oldDistance - 距離) / this._oldDistance)
            }

            (距離 > 5 && 比率 > 0.1)の場合{
                console.log("======== DrawPath の更新====================================================================================")
                (i = 0 とします; i < _self._path.length - 1; i++) {
                    矢印を_self._arrows[i]とします。
                    point1 = _self._path[i]とします。
                    point2 = _self._path[i + 1]とします。
                    リフレッシュ矢印(ポイント1、ポイント2、矢印);
                }
                this._oldDistance = 距離;
                this._oldCameraPos = { x: カメラの位置 x、 y: カメラの位置 y、 z: カメラの位置 z }
            }
        }
    }

    関数drawLine(point1, point2) {
        const位置 = [];

        位置をプッシュします(point1.x / 50、point1.y / 50、point1.z / 50);
        位置をプッシュします(point2.x / 50、point2.y / 50、point2.z / 50)。

        ジオメトリを新しい LineGeometry() にします。
        ジオメトリの位置を設定します。

        matLine = new LineMaterial({
            色: 0x009900、
            線幅: 0.003, // サイズ減衰のあるワールド単位、それ以外の場合はピクセル
            破線: 真、
            深さテスト: _depthTest、
            サイド: _サイド
        });

        line = new Line2(geometry, matLine); とします。
        行の距離を計算します。
        ラインスケールを50, 50, 50に設定します。

        シーンに行を追加します。
        _self._lines.push(行);

    }

    関数drawArrow(point1, point2) {
        _self.createArrowLine(point1, point2) とします。
        var メッシュライン = arrowLine.メッシュライン;

        let canvasTexture = _canvasDraw.drawArrow(THREE, renderer, 300, 100); //矢印 var material = new MeshLineMaterial({
            使用マップ: true、
            マップ: キャンバステクスチャ、
            色: 新しい THREE.Color(0x00f300)、
            不透明度: 1,
            解像度: 新しい THREE.Vector2($(viewerContainerId).width(), $(viewerContainerId).height()),
            線幅: arrowLine.lineWidth、
            深さテスト: _depthTest、
            サイド: _サイド、
            繰り返し: 新しい THREE.Vector2(1, 1)、
            透明: true、
            サイズ減衰: 1
        });

        var mesh = new THREE.Mesh(meshLine.geometry, material);
        メッシュスケールを50に設定(50, 50, 50);
        シーンを追加します(メッシュ);
        _self._arrows.push(メッシュ);

    }

    関数refreshArrow(point1, point2, arrow) {
        _self.createArrowLine(point1, point2) とします。
        var メッシュライン = arrowLine.メッシュライン;

        let canvasTexture = _canvasDraw.drawArrow(THREE, renderer, 300, 100); //矢印 var material = new MeshLineMaterial({
            使用マップ: true、
            マップ: キャンバステクスチャ、
            色: 新しい THREE.Color(0x00f300)、
            不透明度: 1,
            解像度: 新しい THREE.Vector2($(viewerContainerId).width(), $(viewerContainerId).height()),
            線幅: arrowLine.lineWidth、
            深さテスト: _depthTest、
            サイド: _サイド、
            繰り返し: 新しい THREE.Vector2(1, 1)、
            透明: true、
            サイズ減衰: 1
        });

        矢印のジオメトリ = メッシュラインのジオメトリ;
        矢印.material = マテリアル;

    }

    this.createArrowLine = 関数 (point1, point2) {
        centerPoint を { x: (point1.x + point2.x) / 2、 y: (point1.y + point2.y) / 2、 z: (point1.z + point2.z) / 2 } とします。
        距離を utils.distance(point1.x、point1.y、point1.z、point2.x、point2.y、point2.z) とします。

        var startPos = { x: (point1.x + point2.x) / 2 / 50、 y: (point1.y + point2.y) / 2 / 50、 z: (point1.z + point2.z) / 2 / 50 }

        d = utils.distance(centerPoint.x、centerPoint.y、centerPoint.z、camera.position.x、camera.position.y、camera.position.z); とします。
        (d < 2000)の場合d = 2000;
        d > 10000 の場合、d = 10000;
        線幅を 100 * d / 4000 とします。
        //コンソールログ("d=", d);

        sc = 0.035とします。
        var endPos = { x: startPos.x + (point2.x - point1.x) * sc * d / 距離 / 50、 y: startPos.y + (point2.y - point1.y) * sc * d / 距離 / 50、 z: startPos.z + (point2.z - point1.z) * sc * d / 距離 / 50 }

        var arrowLinePoints = [];
        矢印の線ポイントをプッシュします(開始位置 x、開始位置 y、開始位置 z);
        矢印の線分ポイントをプッシュします(endPos.x、endPos.y、endPos.z);

        var meshLine = 新しい MeshLine();
        メッシュラインにジオメトリを設定します。

        戻り値: meshLine, lineWidth: lineWidth };
    }

    this.setDepthTest = 関数 (bl) {
        もし(bl){
            _depthTest = true;
            this._lines.map(行 => {
                ライン.マテリアル.深さテスト = true;
                ライン.マテリアル.サイド = 0;
            });
            this._arrows.map(矢印 => {
                矢印.material.depthTest = true;
                矢印.material.side = 0;
            });
        } それ以外 {
            _depthTest = false;
            this._lines.map(行 => {
                ライン.マテリアル.深さテスト = false;
                ライン.マテリアル.サイド = THREE.DoubleSide;
            });
            this._arrows.map(矢印 => {
                矢印.material.depthTest = false;
                arrow.material.side = THREE.DoubleSide;
            });
        }
    }

    /**
     * キャンセル */
    this.undo = 関数(){
        scene.remove(this._lines[this._lines.length - 1]);
        scene.remove(this._arrows[this._arrows.length - 1]);
        _self._path.splice(this._path.length - 1, 1);
        _self._lines.splice(this._lines.length - 1, 1);
        _self._arrows.splice(this._arrows.length - 1, 1);
    }

}

DrawPath プロトタイプ コンストラクター = DrawPath;

エクスポート {DrawPath}

show.js のコードの一部:

パスを描画します。

    //線を描画する drawPath = new DrawPath();
    描画パス.config(
        オブジェクト、
        カメラ、
        シーン、
        振り向く
    );

    $("#rightContainer").show();
    $("#line-start").on("click", 関数(イベント) {
        描画パスを開始します。
    });
    $("#line-stop").on("click", 関数(イベント) {
        描画パスを停止します。
    });
    $("#line-undo").on("click", 関数(イベント) {
        描画パスを元に戻す();
    });
    $("#line-show").on("click", 関数(イベント) {
        描画パスを更新します。
    });
    depthTest を true にします。
    $("#line-depthTest").on("click", 関数(イベント) {
        if (深さテスト) {
            パスを描画します。
            深さテスト = false;
        } それ以外 {
            深度テストを true に設定します。
            深さテスト = true;
        }
    });

間隔を設定する(() => {
    描画パス && 描画パス.refresh();
}, 100);

効果画像:

まだいくつか問題があります:

このレンダリングではシーンがズームインされ、矢印が少し大きくなっていますが、最大サイズは制御されています。形状に問題があるだけで、これは遠近法の問題である可能性があります。

私が期待する効果は次のようになります。つまり、どの角度から見ても矢印が変形しないはずです。

これで、人生の半分を費やした three.js を使用して 3 次元矢印線を描く方法についての記事は終わりです。three.js 3 次元矢印線に関するその他の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援してください。

以下もご興味があるかもしれません:
  • Three.js が Facebook Metaverse 3D ダイナミック ロゴ効果を実現
  • three.js を使用してクールなアシッドスタイルの 3D ページ効果を実現します
  • three.js で 3D ダイナミック テキスト効果を実現する方法
  • 露滴アニメーション効果を実装するための Three.js サンプル コード
  • three.js でのマルチスレッドの使用とパフォーマンステストの詳細な説明
  • JavaScript Three.js でテキストを作成する最初の経験

<<:  きちんとした標準的なHTMLタグの書き方を学ぶ

>>:  CSS3のボックスサイズプロパティの興味深いボックスモデルについての簡単な説明

推薦する

CentOS7 に MySQL データベースをインストールしてデバッグする詳細な手順 [例]

この例では、デバッグ用の MySQL データベースをダウンロードしてインストールする必要があります。...

ブリージングカルーセルを実装するネイティブJS

今日は、ネイティブ JS で実装されたブリージング カルーセルを紹介します。効果は次のとおりです。 ...

MySQL マスターとスレーブの不整合とその解決策の詳細な説明

1. MySQL マスタースレーブ非同期1.1 ネットワーク遅延MySQLのマスタースレーブレプリケ...

Hadoop 2.x と 3.x の 22 ポイントの比較、Hadoop 3.x の 2.x に対する改善点

質問ガイド1. Hadoop 3.x はどのようにして障害を許容するのでしょうか? 2. Hadoo...

CentOS はローカル yum ソースを使用して LAMP 環境を構築するグラフィック チュートリアル

この記事では、ローカル yum ソースを使用して CentOS 上に LAMP 環境を構築する方法に...

LinuxカーネルマクロContainer_Ofの詳細な説明

目次1. 構造体はメモリにどのように保存されますか? 2. container_ofマクロ3. 型4...

VirtualBox の仮想ディスク vdi ファイルの容量を拡張する方法 (グラフィック チュートリアル)

VirtualBoxのインストールディレクトリを見つけます。ディレクトリ内には容量を拡張するために...

JSは円形のプログレスバーのドラッグとスライドを実装します

この記事の例では、円形のプログレスバーのドラッグアンドスライドを実現するための具体的なJSコードを紹...

CSS は、モバイル端末でクリックされたときに生成された要素の背景色を削除します (推奨)

クリック時に背景色を生成する要素の CSS スタイルに次のコードを追加します。 -webkit-ta...

Vueモバイル端末の適応化問題の詳細説明

1. vue uiでプロジェクトを作成する 2. 基本設定項目を選択する 3. プロジェクトを実行す...

Docker で nginx のログレベルを調整する方法

目次はじめにNginx Dockerファイル新しい会議もっと参考文献はじめに最近、アプリケーションの...

Linux システムに Zookeeper サービスをインストールする方法

1. /usr/local/services/zookeeper フォルダを作成します。 mkdir...

Vueコンポーネント間の通信の非常に詳細な要約

目次序文1. Props、$emit一方向データフロー2. $親、$子3. $attrs、$list...

Dockerイメージの階層化の原理の詳細な説明

ベースイメージベースイメージには 2 つの意味があります。他のイメージに依存せず、ゼロから構築します...

Dockerコマンドの学習を1つの記事にまとめる

目次導入ミラーリポジトリログイン引く押す検索ローカル画像管理画像rmiタグ建てる歴史保存負荷輸入コン...