0
votes

I'm trying to keep an object (a sphere) centered in front of a camera as the camera moves around. I'm animating the camera by changing it's position and it's rotation with lookAt() which seems to work correctly. But the sphere object just won't say in the frame. I've tried

const centerSphereInCameraWithQuaternion = (sphere: THREE.Mesh, camera: THREE.PerspectiveCamera) => {
  const vec = new THREE.Vector3( 0, 0, - 4 );
  vec.applyQuaternion( camera.quaternion );

  sphere.position.copy( vec );
}

which causes the sphere to disappear entirely. I've also tried

const centerSphereInCamera = (sphere: THREE.Mesh, camera: THREE.PerspectiveCamera) => {
  sphere.position.copy( camera.position );
  sphere.rotation.copy( camera.rotation );
  sphere.updateMatrix();
  sphere.translateZ( - 4 );
}

which almost works, but the sphere rotates in the opposition direction of the cameras facing. Also tried

sphere.position.sub(camera.position);

which doesn't show anything at all.

I've tried adding a camera helper to make sure the camera's world matrix is being updated correctly, and it seems like it is.

What am I doing wrong here? Is there a way I can accomplish this without adding the mesh as a child of the camera?

1
@WestLangley Is there a way I can do this without adding the mesh as a child of camera? I tried the other solution you linked, but that caused the sphere to disappear.zakdances
Disappear? Then you are doing something wrong...WestLangley
@WestLangley After some investigation, I started applying the transform to the sphere after moving the camera and now it's working, so it looks like I was doing something wrong.zakdances

1 Answers

0
votes

Unless you're doing something unorthodox with the camera's position, all you really need to hold an object in front of the camera (without nesting it within the camera) is three lines:

sphere.position.copy(camera.position);
sphere.rotation.copy(camera.rotation);
sphere.translateZ(-40);

You can run the code snippet below to see it in action. I made the camera glide all over the scene, then used the three lines above to keep the sphere directly in front of the camera on each frame. The relevant code is in the animate() function near the bottom:

var camera, scene, renderer;

// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xe1e1e1);

// camera
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1,       100);

const ambient = new THREE.DirectionalLight(0xddeeff, 1);
ambient.position.set(0, 1, 1);

const floor = new THREE.Mesh(
    new THREE.BoxBufferGeometry(20, 0.25, 20),
    new THREE.MeshPhongMaterial()
);
const wall = new THREE.Mesh(
    new THREE.BoxBufferGeometry(.5, 3, 10), 
    new THREE.MeshPhongMaterial({ color: 0xc4ffc4})
);
const sphere = new THREE.Mesh(
    new THREE.SphereBufferGeometry(3, 20, 20),
    new THREE.MeshPhongMaterial({ color: 0xff0000 })
);

scene.add(ambient, floor, wall, sphere);

renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, Infinity));

document.body.appendChild(renderer.domElement);

function animate(t) {
    // Move camera
    const xPos = Math.sin(t / 777) * 50;
    const yPos = Math.sin(t / 666) * 25;
    const zPos = Math.cos(t / 1000) * 50;
    camera.position.set(xPos, yPos, zPos);
    camera.lookAt(0, 0, 0);
    
    // Move sphere
    sphere.position.copy(camera.position);
    sphere.rotation.copy(camera.rotation);
    sphere.translateZ(-40);
    
    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}

animate(0);
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>