How to generate points that are always planar to the camera?

Assume a basic function that generates polar 2D points like so:

function generatePoints(radius, numPoints, camera) {
  const points = [];

  for (let i = 0; i < numPoints; i++) {
    const angle = (i / numPoints) * Math.PI * 2;

    // calculate world coordinates of point
    const x = radius * Math.cos(angle);
    const y = radius * Math.sin(angle);
    const z = 0;

    // how to transform point to be planar with respect to camera?
    const endPoint = new THREE.Vector3(x, y, z);
    
    points.push(endPoint);
  }

  return points;
}

I have a camera set at [0,0,150] facing [0,0,0] with OrbitControls so it can rotate around the origin. I’d like to be able to call my function above at any time and have it generate those points so the whole form is planar to the camera, no matter where it is.

I thought to try:

const endPoint = new THREE.Vector3(x, y, z);
    endPoint.applyMatrix4(camera.matrixWorldInverse);
    endPoint.applyMatrix4(camera.projectionMatrix);

But that didn’t work at all

By default, the camera’s near and far planes are coplanar with XY world plane. Then, when the camera rotates, its quaternion is applied to the local camera’s coordinate system.

So you could create points in XY plane and then apply the camera quaternion to them, they will be coplanar with the camera’s near/far planes (rotated exactly as the camera).

this piece does the trick:

 for(let i = 0; i < points.length; i++) {
    const pnt = points[i].clone().applyQuaternion(camera.quaternion);
    const off = i * 3;
    vert[off] = pnt.x;
    vert[off + 1] = pnt.y;
    vert[off + 2] = pnt.z;
  }
  pos_attr.needsUpdate = true;

In general, if you need something to be coplanar with the camera’s near/far planes, you can use NDC space, there are project / unproject functions for Vector3: three.js docs

Also you might find Sprites useful, they are always coplanar: three.js docs

Thanks, I ended up simplifying to this:

make2DPoint: function(x,y,z) {
const endPoint = new THREE.Vector3(x, y, z);
endPoint.applyQuaternion(Three.camera.quaternion);
return endPoint;
}

which works great. FYI I’m aware of sprites but my goal wasn’t to make each individual particle necessarily billboard-esque, but rather have the entire arrangement of points appear perpendicular to the camera — which this does well :slight_smile:

1 Like