Three.jsのSelectionBoxをOrthographicCameraで使いたい

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では動作しません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です