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のボックスサイズプロパティの興味深いボックスモデルについての簡単な説明

推薦する

Vue でコンポーネントを一括インポート、登録、使用する方法

序文コンポーネントは、非常に頻繁に使用されるものです。多くの人は、コンポーネントを 1 つのファイル...

最新バージョンMySQL5.7.19 解凍版インストールガイド

MySQL バージョン: MySQL Community Edition (GPL) ------ ...

Dockerでイメージをプルするための手順を完了する

1. Docker pullはイメージをプルします$ docker pull {IMAGE_NAME...

ウェブページのエンコードにおける GB2312、GBK、UTF-8 の違い

まず、GB2312、GBK、UTF-8 はすべて文字エンコーディングであることを理解する必要がありま...

javascript:void(0) の意味と使用例

voidキーワードの紹介まず、void キーワードは JavaScript で非常に重要なキーワード...

Vue3 トランジションアニメーションの落とし穴記録について

目次背景問題の場所さらなる分析要約する背景私のコース「Vue 3 エンタープライズレベルの音楽アプリ...

Linux での Firewalld の高度な設定の使用に関する詳細な説明

IPマスカレードとポート転送Firewalldは2種類のネットワークアドレス変換をサポートしています...

MySQLデータベースを使い始めるための最初のステップはテーブルを作成することです

データベースを作成する右クリック - 新しいデータベースを作成ライブラリ名を入力し、文字セットと並べ...

vue3.0 プロジェクトを素早く構築するための手順を完了する

目次1. 3.0をより適切にサポートするには、vue/cliのバージョンが4.5.0以上であることを...

Select はダブルクリック dbclick イベントをサポートしていません

XML/HTML コードコンテンツをクリップボードにコピー< div クラス= "c...

写真とテキストによる MySQL 8.0.11 インストール チュートリアル

インターネット上には多くのチュートリアルがありますが、基本的には同じです。ただし、細かい原因でソフト...

【HTML要素】画像の埋め込み方法

img 要素を使用すると、HTML ドキュメントに画像を埋め込むことができます。画像を埋め込むには、...

発生したブラウザの互換性の問題と解決策(推奨)について

序文:先週の日曜日、先輩から3ページ作るのを手伝って欲しいと頼まれました。データのやり取りなどはなく...

HTMLページ埋め込み動画とJSコントロール切り替え動画例の詳しい説明

まず、ページにビデオを埋め込むための HTML コードは次のとおりです。コードをコピーコードは次のと...