Three.jsで矩形範囲内の3次元オブジェクトを選択させたい。
どのように実現するか検討していたら、Three.jsのexamplesにboxselectionというサンプルがあった。
Three.js examples misc boxselection
配色が派手でわかりにくいが、ドンピシャである。
早速使ってみたが、これはPerspectiveCamera専用でOrthographicCameraでは使えないと判明。
残念。
しばしググると以下のサイトを発見。
how to set frustum inortographic camera THREEJS
boxselectionをOrthographicCameraでも使えるようにしている。
しかも選択色にシェーダを使い、完成度が高い。
Stack Overflowの投稿者の方、有難うございます。
以下を変更すればOrthographicCameraになる、と書いてある。
変更前。
function init() { camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 5000 ); //frustumSize = 1000; //var aspect = window.innerWidth / window.innerHeight; //camera = new THREE.OrthographicCamera( frustumSize * aspect / - 2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1000 );
変更後。
function init() { //camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 5000 ); frustumSize = 1000; var aspect = window.innerWidth / window.innerHeight; camera = new THREE.OrthographicCamera( frustumSize * aspect / - 2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1000 );
使ってみた。
※配色はシアンに統一しています。
あれ、うまくいかない。
選択範囲の矩形と実際に選択されるオブジェクトがずれています。
よく見ると以下の一文。
In perspective camera it works fine but not in ortographic camera Best regards
「PerspectiveCameraでは正常に機能するが、OrthographicCameraでは機能しない」との事。
世の中甘くない。
ソースを見てみる。
selectionBox.createFrustumという関数が、画面で指定する矩形から、画面奥に視体積を作成している。
この視体積に含まれるオブジェクトを選択対象としているようだ。
ここが怪しい。
多分、視体積が透視投影のままだからだ。
ここを正投影に変えてみる。
変更前。
var planeTop = new THREE.Plane(); planeTop.setFromCoplanarPoints( vecNear, vecTopLeft, vecTopRight ); var planeRight = new THREE.Plane(); planeRight.setFromCoplanarPoints( vecNear, vecTopRight, vecDownRight ); var planeDown = new THREE.Plane(); planeDown.setFromCoplanarPoints( vecDownRight, vecDownLeft, vecNear ); var planeLeft = new THREE.Plane(); planeLeft.setFromCoplanarPoints( vecDownLeft, vecTopLeft, vecNear ); var planeFront = new THREE.Plane(); planeFront.setFromCoplanarPoints( vecTopRight, vecDownRight, vecDownLeft ); var planeBack = new THREE.Plane(); planeBack.setFromCoplanarPoints( vectemp3, vectemp2, vectemp1 ); planeBack.normal = planeBack.normal.multiplyScalar( -1 );
変更後。
var vecTopLeftBack = new THREE.Vector3(tmpPoint.x, tmpPoint.y, wdepth); var vecTopRightBack = new THREE.Vector3(endPoint.x, tmpPoint.y, wdepth); var vecDownRightBack = new THREE.Vector3(endPoint.x, endPoint.y, wdepth); var vecDownLeftBack = new THREE.Vector3(tmpPoint.x, endPoint.y, wdepth); vecTopLeftBack.unproject(selectionBox.camera); vecTopRightBack.unproject(selectionBox.camera); vecDownRightBack.unproject(selectionBox.camera); vecDownLeftBack.unproject(selectionBox.camera); var planeTop = new THREE.Plane(); planeTop.setFromCoplanarPoints(vecTopLeftBack, vecTopRightBack, vecTopRight); var planeRight = new THREE.Plane(); planeRight.setFromCoplanarPoints(vecTopRight, vecTopRightBack, vecDownRightBack); var planeDown = new THREE.Plane(); planeDown.setFromCoplanarPoints(vecDownRight, vecDownRightBack, vecDownLeftBack); var planeLeft = new THREE.Plane(); planeLeft.setFromCoplanarPoints(vecTopLeftBack, vecTopLeft, vecDownLeft); var planeFront = new THREE.Plane(); planeFront.setFromCoplanarPoints(vecTopRight, vecDownRight, vecDownLeft); var planeBack = new THREE.Plane(); planeBack.setFromCoplanarPoints(vecTopLeftBack, vecDownLeftBack, vecDownRightBack);
wdepthはfrontの投影面を後ろに下げるため、少し前で以下のように定義している。
var wdepth = 1000; vecTopLeft.z = -wdepth; vecTopRight.z = -wdepth; vecDownRight.z = -wdepth; vecDownLeft.z = -wdepth;
この値はnear-farの区間距離にすれば良いと思う。
試してみる。
うまくいった。
修正版は以下です。
ダブルクリック(またはESC)でOrbitControlsの制御と矩形選択モードがトグルします。
SelectionBox改良版
※IE、Edgeでは動作しません。