How to rotate the object the same regardless of the direction of the camera?

Viewed 52

I need to rotate an object along the world axis taking into account the direction of the camera so that the rotation stays the same when changing the direction of the camera.

In the following code, you can rotate the sphere along the world axis, but when the camera rotates, the rotation will be the same and this is not what I need. How to take into account the direction of the camera when rotating the sphere?

const viewport = { width: window.innerWidth, height: window.innerHeight };

// scene preparation
let canvas, renderer, camera, scene, controls;

{
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(viewport.width, viewport.height);
  renderer.setPixelRatio(window.devicePixelRatio);
  canvas = renderer.domElement;
  document.body.appendChild(canvas);
  document.body.style.margin = '0px';
}

{
  const fov = 45;
  const aspect = viewport.width / viewport.height;
  const near = 1;
  const far = 100;
  camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.lookAt(0, 0, 0);
  camera.position.z = 10;
}

{
  scene = new THREE.Scene();
}

{
  controls = new THREE.OrbitControls(camera, canvas);
  controls.enableDamping = false;
  controls.enableZoom = true;
  controls.enableRotate = false;
  controls.enablePan = false;
  controls.autoRotate = true;
}

// adding scene objects
let axesHelper, light, light1, light2, sphere, texture;

{
  axesHelper = new THREE.AxesHelper(10);
  scene.add(axesHelper);
}

{
  light = new THREE.HemisphereLight(0xffffff, 0x000000);
  light1 = new THREE.PointLight(0xffffff);
  light1.position.set(10, 0, 0);
  light2 = new THREE.PointLight(0xffffff);
  light2.position.set(-10, 0, 0);
  scene.add(light);
  scene.add(light1);
  scene.add(light2);
}

{
  texture = new THREE.TextureLoader().load('https://threejs.org/manual/examples/resources/images/wall.jpg');
  const geometry = new THREE.SphereBufferGeometry(5, 32, 16);
  const material = new THREE.MeshPhongMaterial({ color: 0xff0000, map: texture });
  sphere = new THREE.Mesh(geometry, material);
  scene.add(sphere);
}

// sphere rotation
const prevCoords = new THREE.Vector2();
const deltaCoords = new THREE.Vector2();
function handleEvent(event) {
  const isFirst = event.type === 'mousedown';
  const isLast = event.type === 'mouseup';
  if(isFirst) {
    this.moving = true;
    prevCoords.set(event.clientX, event.clientY);
  }
  else if(isLast) {
    this.moving = false;
  }
  else if(!this.moving) {
    return;
  }
  deltaCoords.set(event.clientX - prevCoords.x, event.clientY - prevCoords.y);
  rotateSphere();
  prevCoords.set(event.clientX, event.clientY);
}
const vector = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
let axis, angle;
function rotateSphere() {
  // camera.getWorldDirection(vector);
  // quaternion.setFromAxisAngle(vector, deltaCoords.x * 0.01);
  // sphere.quaternion.premultiply(quaternion);
  
  sphere.rotateOnWorldAxis(new THREE.Vector3(1, 0, 0), deltaCoords.y * 0.001);
  sphere.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), deltaCoords.x * 0.001);
}
window.addEventListener('mousedown', handleEvent);
window.addEventListener('mousemove', handleEvent);
window.addEventListener('mouseup', handleEvent);

// scene rendering
function loop(time) {
  controls.update();
  renderer.render(scene, camera);
  window.requestAnimationFrame(loop);
}
window.requestAnimationFrame(loop);
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

I found how to get camera direction:

camera.getWorldDirection(vector);

But I don’t understand how I can use this direction vector to fix the axis change when the camera direction changes

1 Answers

I don't know exactly what I want to achieve. I think you want to rotate around the x-axis in view space instead of the x-axis in world space. Use the camera object and .localToWorld to transform a vector form world space to view space:

let xAxis = camera.localToWorld(new THREE.Vector4(1, 0, 0, 0));
sphere.rotateOnWorldAxis(xAxis, deltaCoords.y * 0.001);

const viewport = { width: window.innerWidth, height: window.innerHeight };

// scene preparation
let canvas, renderer, camera, scene, controls;

{
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(viewport.width, viewport.height);
    renderer.setPixelRatio(window.devicePixelRatio);
    canvas = renderer.domElement;
    document.body.appendChild(canvas);
    document.body.style.margin = '0px';
}

{
    const fov = 45;
    const aspect = viewport.width / viewport.height;
    const near = 1;
    const far = 100;
    camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.lookAt(0, 0, 0);
    camera.position.z = 10;
}

{
    scene = new THREE.Scene();
}

{
    controls = new THREE.OrbitControls(camera, canvas);
    controls.enableDamping = false;
    controls.enableZoom = true;
    controls.enableRotate = false;
    controls.enablePan = false;
    controls.autoRotate = true;
}

// adding scene objects
let axesHelper, light, light1, light2, sphere, texture;

{
    axesHelper = new THREE.AxesHelper(10);
    scene.add(axesHelper);
}

{
    light = new THREE.HemisphereLight(0xffffff, 0x000000);
    light1 = new THREE.PointLight(0xffffff);
    light1.position.set(10, 0, 0);
    light2 = new THREE.PointLight(0xffffff);
    light2.position.set(-10, 0, 0);
    scene.add(light);
    scene.add(light1);
    scene.add(light2);
}

{
    texture = new THREE.TextureLoader().load('https://threejs.org/manual/examples/resources/images/wall.jpg');
    const geometry = new THREE.SphereBufferGeometry(5, 32, 16);
    const material = new THREE.MeshPhongMaterial({ color: 0xff0000, map: texture });
    sphere = new THREE.Mesh(geometry, material);
    scene.add(sphere);
}

// sphere rotation
const prevCoords = new THREE.Vector2();
const deltaCoords = new THREE.Vector2();
function handleEvent(event) {
    const isFirst = event.type === 'mousedown';
    const isLast = event.type === 'mouseup';
    if (isFirst) {
        this.moving = true;
        prevCoords.set(event.clientX, event.clientY);
    }
    else if (isLast) {
        this.moving = false;
    }
    else if (!this.moving) {
        return;
    }
    deltaCoords.set(event.clientX - prevCoords.x, event.clientY - prevCoords.y);
    rotateSphere();
    prevCoords.set(event.clientX, event.clientY);
}
const vector = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
let axis, angle;
function rotateSphere() {
    // camera.getWorldDirection(vector);
    // quaternion.setFromAxisAngle(vector, deltaCoords.x * 0.01);
    // sphere.quaternion.premultiply(quaternion);

    let xAxis = camera.localToWorld(new THREE.Vector4(1, 0, 0, 0));
    sphere.rotateOnWorldAxis(xAxis, deltaCoords.y * 0.001);
    sphere.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), deltaCoords.x * 0.001);
}
window.addEventListener('mousedown', handleEvent);
window.addEventListener('mousemove', handleEvent);
window.addEventListener('mouseup', handleEvent);

// scene rendering
function loop(time) {
    controls.update();
    renderer.render(scene, camera);
    window.requestAnimationFrame(loop);
}
window.requestAnimationFrame(loop);
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Related