Three.js でピッキング(3Dモデルをマウスで選択)してみた

phiary に引っ越しました. 毎日プログラミングやWebに関する情報を発信しています! RSS 登録してたまに覗いたり, tweet やハテブして拡散してもらえると幸いです.

Pocket

久々の 3D プログラミング.

Three.js を使ってピッキングしてみたので, 今回はそのやり方について簡単に解説したいと思います.

サンプルは jsdo.it で作ってあるので, 実際に動かしたり, fork してイジってもらえると嬉しいです.

table of contents

キッカケ

昨日, @kino_hideyoshi さんから下記のような質問を頂きました.

Three.js 自体ご無沙汰だったので, 復習がてらサンプルを作った次第です.

最近は, ほぼ毎日 TwitterMail で質問を頂けるようになってきた.

もともと仕事では, ずっと 3D ゲームを作っていたので わりとこういう3D数学をガリガリ使うようなプログラミングの方が得意だったりします.

なのでみなさん, JavaScript に限らず気軽に質問して頂けると幸いです.

up

サンプル

サンプルです. マウスをオブジェクトに乗せると色が変わります.

up

ソースコード

ピッキング部分の実装コードです.

/*
 * マウス移動
 */
var onmousemove = function(e) {
    var rect = e.target.getBoundingClientRect();
    // マウス位置(2D)
    var mouseX = e.clientX - rect.left;
    var mouseY = e.clientY - rect.top;
    
    // マウス位置(3D)
    mouseX = (mouseX/SCREEN_WIDTH) *2 - 1;
    mouseY =-(mouseY/SCREEN_HEIGHT)*2 + 1;
    
    // マウスベクトル
    var pos = new THREE.Vector3(mouseX, mouseY, 1);
    // pos はスクリーン座標系なので, オブジェクトの座標系に変換
    projector.unprojectVector(pos, camera);
    
    // 始点, 向きベクトルを渡してレイを作成
    var ray = new THREE.Raycaster(camera.position, pos.subSelf(camera.position).normalize());
    // 交差判定
    var obj = ray.intersectObjects([mesh]);
    
    material.color.setRGB(1, 1, 1);
    if (obj.length > 0) {
        // 交差していたら色を変更する
        material.color.setRGB(1, 0, 0);
    }
};

解説

簡単に解説します. よかったら参考にしてください.

マウス位置を求める(Line 5~8)

マウスの 2D 座標を求めています. x 軸は 0~スクリーン幅, y 軸は 0~スクリーン高さになります.

マウス座標を 3D 化する(Line 10~12)

3D 化するという表現は微妙ですが, あえてここではそう表現しました. 上記で述べたようにマウスでクリックした位置は 2D 座標値となっています.

そこで下記のような計算をすることでX軸, Y軸共に -1 ~ 1 の間に収まるよう 調整しています.

    // マウス位置(3D)
    mouseX = (mouseX/SCREEN_WIDTH) *2 - 1;
    mouseY =-(mouseY/SCREEN_HEIGHT)*2 + 1;

マウス位置から 3D ベクトルを作成(Line 15~17)

THREE.Vector3 クラスを使って先ほど求めた X, Y 値のベクトルを作成します. それを Projector の unprojectVector メソッドを使ってスクリーン座標系からオブジェクト座標系に変換します.

レイを作成(Line 20)

Three.js には THREE.Raycaster というクラスが用意されており, これを使うことでレイを定義することができます. 引数は, 第一引数に開始位置を, 第二引数に向きベクトルを渡します.

var ray = new THREE.Raycaster(camera.position, pos.subSelf(camera.position).normalize());

レイとメッシュの交差判定(Line 22)

THREE.Raycaster クラスには intersectObjects という交差判定を行うメソッドが用意されています. 第一引数にメッシュのリストを渡すことで, 全てのメッシュとの交差判定を行うことができ, 交差したメッシュのみのをリストとして返します.

簡単に使えて超便利ですね♪

var obj = ray.intersectObjects([mesh]);

※注意点としては, あくまで intersectObjects に渡すのはメッシュのリストだということです. mqo.three.js などで読み込んで生成したオブジェクトはメッシュではないので, 下記のように children プロパティを渡すようにしてください.

var obj = ray.intersectObjects(mqoModel.children);

衝突結果の取得(Line25)

intersectObjects の結果の length が 0 より大きければ衝突しているということです. この条件が通った際に, ページの遷移だったり文字列の表示などを行えば3D UI として使えます!!

以上ざっくりと解説しました.

今回は mousemove イベントで交差判定を行いましたが, click や mousedown イベントに変更してもらえればクリックしたときのみ判定することができます.

up

TRACK BACK URL

POST COMMENT

メールアドレスが公開されることはありません。

COMMENT

  • toshi0104 より:

    Three.js でピッキング(3Dモデルをマウスで選択)してみた | TM Life

    久々の 3D プログラミング. Three.js を使ってピッキングしてみたので, 今回はそのやり方について簡単に解説したいと思います.
    サンプルは jsdo.it で作ってあるので, 実際に動かしたり, fork してイジってもらえると嬉しいです. table of
    contents キッカケ サンプル ソースコード 解説 マウス位置を求める(Line 5~8) マウス座標を 3D 化する(Li…

    はてなブックマーク – Three.js でピッキング(3Dモデルをマウスで選択)してみた | TM Life はてなブックマークに追加

  • typista より:

    ぽけったー Three.js でピッキング(3Dモデルをマウスで選択)してみた

  • 匿名 より:

    参考になりました。ありがとうございます