How to project object centroid to 2D space while orbiting

Hi Everyone,

I am pretty new to three.js and I have run into a problem and am wondering if I am doing something wrong.

I would like to map absolutely positioned interactive react components to the centroids of some 3D objects in my scene to label them. My overall approach is to add a change listener to my orbit controls, then in the change listener get the 3D centroids of my labeled objects by getting the center of their bounding boxes. Then project those center points using the camera, then transform those returned coordinates using:

const componentX = (this.WIDTH + projectedPoint.x * this.WIDTH) / 2;
const componentY = (this.HEIGHT - projectedPoint.y * this.HEIGHT) / 2;

where WIDTH and HEIGHT hold the size of the three JS canvas. And then finally to use the resulting coordinates as the left and top for my react components. Presumably, as I orbit, the components should stick nicely to the 3D objects.

To sanity check this approach, I’ve positioned a (1, 1, 1) cube at (-0.5, -0.5, -0.5) and positioned my camera to look at (0, 0, 0). With a canvas size of (1600, 1200), when I orbit around the origin I expect (componentX, componentY) to stay at (800, 600) or very very close to it, but this is not what I am seeing.

When I orbit very slowly it stays close to (800, 600), something like (801.502479043716, 598.4568062234692) is typical. But the faster I move the mouse to do the orbiting, the less accurate the reading becomes, for example (713.3946953743033, 519.4400754464716) is typical, but I’ve had values close to (1500, 400).

Here is the code in my change listener. I’ve even hardcoded the box center point to simplify further:

controls.addEventListener('change', () => {
      const center3d = new THREE.Vector3(0, 0, 0);
      const projectedPoint = center3d.project(this.camera);

      const center2d = new THREE.Vector2(
        (this.WIDTH + projectedPoint.x * this.WIDTH) / 2,
        (this.HEIGHT - projectedPoint.y * this.HEIGHT) / 2);

      console.log('center2d: ' + center2d.x + ', ' + center2d.y);

      this.composer.render();
});

It looks to me like

center3d.project(this.camera);

is simply returning the wrong value when the orbit controls manipulate the camera rapidly. But I have a hard time believing this can actually happen in a library as well used as three.js. So what am I doing wrong? :slight_smile: Is this not a good approach to solving my original problem? Am I misusing Vector3::project? Should I not be making calls involving the camera in the orbit controls’ change handler?

Any help would be much appreciated!
Thanks!

If I understood you correctly - you can try just using CSS2DRenderer together with react.

Create CSS2DObjects (three does the math for you, and automatically aligns them with the 3D objects for you.) and save their references using useRef (or just React refs.) Then, since CSS2DObjects are simply HTML DOM nodes, you can render React components inside them using React portals.