Camera rotation with ArcballControls

Hi.

I’m using ArcballControls but I also want to control the camera programmatically. The user should be able to control the viewpoint with the mouse and jump to specific views by clicking a button.

The documentation of ArcballControls says that after “manual changes to the camera’s transform” the update() method must be called. This works fine when changing the camera’s position and when changing the controls’ target. The only thing not working is the camera’s rotation. After calling update() the camera’s rotation resets to the values from before the manual change.

Am I doing something wrong?

Is this the desired behavior of the ArcballControls? If not, what is the reason? Is updating the rotation particularly complicated and/or expensive?

Thank you!

Edit: I’m only interested in rotation around the z-axis. Changing the x and y rotation would mean changing the target of the controls.

The entire point of controls is to do the rotations for you. After adding controls to the scene, the only way you should be controlling the rotation is by modifying the target (controls’ job is to override the rotation so that the camera looks at the target - if you modify the rotation manually, the camera won’t be looking at the target anymore :sweat_smile:)

Not really - because instead, it’s just impossible :relieved:

1 Like

I understand how the x and y rotation of the camera determines where the camera looks at. If I want the camera to look somewhere else I have to change the target.

But what about the z-rotation? I visualized the problem:

The camera looks at the same point in both images, i.e. the target does not change. It simply rotates around its own z-axis or changes its up vector if you will.

Am I missing something?

If the z-rotation somehow does change the target, how would I manipulate the target to achieve the z-rotation? As the target is just a Vector3 I don’t think this is possible.

Changing the up vector of the camera and calling update() worked:)

@moritzhertler could you please share an example of how you achieved it? I was trying to do something similar with OrbitControls, but ended up rotating an object.

Sure. Here is a quick example:

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <style>
            body {
                margin: 0;
            }
        </style>
    </head>
    <body>
        <input
            type="range" id="rotation" value="0" min="-360" max="360" step="0.01"
            style="position: absolute; left: 50%; bottom: 2em; transform: translateX(-50%);"
        />
        <script type="module" src="/main.js"></script>
    </body>
</html>

main.js

import * as THREE from "three";
import { ArcballControls } from "three/addons/controls/ArcballControls.js";

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight
);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

scene.add(new THREE.AxesHelper(3));
camera.position.z = 4;

const controls = new ArcballControls(camera, renderer.domElement, scene);

const DEGREES_TO_RADIANS = Math.PI / 180;

const input = document.getElementById("rotation");
input.addEventListener("input", (e) => {
    const radians = e.target.value * DEGREES_TO_RADIANS;
    camera.up = new THREE.Vector3(Math.cos(radians), Math.sin(radians), 0);
    controls.update();
});

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

animate();

This should work with OrbitControls as well.

1 Like