Three.jsのcanvas要素にHTMLの表示を重ねたい時があります。

そのときに必要となるのが、canvas要素上のオブジェクトの座標です。これは、描写面であるcanvas要素上の座標のことなので、スクリーン座標と言います。

スクリーン座標の算出方法

THREE.Cameraクラスのproject()メソッドを使うことで、ステージ上のXY座標に変換できます。

// object3D は任意の3Dオブジェクト。
// 3Dオブジェクトのワールド座標を取得する
const worldPosition = object3D.getWorldPosition(new THREE.Vector3());
// スクリーン座標を取得する
const projection = worldPosition.project(camera);

project()メソッドの戻り値はTHREE.Vector3D型で、xyはそれぞれ-1.0+1.0の値をとります。zは3Dオブジェクトの深度を示します。ワールド座標を計算する必要があるので、計算対象の3Dオブジェクトはシーンに追加されている必要があります(シーン直下でなくても問題ありません)。

2D座標の取得には、rendererインスタンスの幅・高さの値を計算することでcanvas要素の左上からの座標を求めることができます。

const object3D = new THREE.Mesh(); // 任意の3Dオブジェクト
const width = 960; // rendererの仮サイズ(利用場面において適したサイズに変更ください)
const height = 540;

// 3Dオブジェクトのワールド座標を取得する
const worldPosition = object3D.getWorldPosition(new THREE.Vector3());
// スクリーン座標を取得する
const projection = worldPosition.project(camera);
const sx = (width / 2) * (+projection.x + 1.0);
const sy = (height / 2) * (-projection.y + 1.0);

// スクリーン座標
console.log(sx, sy);

スクリーン座標算出のサンプル

次のサンプルは球体が3D空間内を円周上を移動している様子を表現したものですが、球体のスクリーン座標をテキストで表示するようにしています。

このサンプルではcanvasに重ねて任意のdiv要素を重ね合わせます。座標の原点をあわせておきたいので、CSSのposition:absoluteで2つの要素の絶対座標をリセットしておきます。親のdiv要素の原点を基点としたいのでposition:relativeを指定します。

<!-- 親のタグの基準点をリセット -->
<div style="position:relative;">
  <!-- Three.js用のcanvasタグ -->
  <canvas style="position: absolute; top: 0; left: 0;"></canvas>

  <!-- 座標表示用のdivタグ -->
  <div style="position: absolute; top: 0; left: 0;"></div>
</div>