Limiting the rotation of a camera in three.js

After spending a long night working with quaternions, I finally managed to implement mouselook functionality on a test game using three.js.

However, I quickly noticed that if I continue looking upwards, the camera ends up flipping upside down. I hastily put together some code that works fine when the user scrolls leisurely, but fails when scrolling fast. I suspect it's an issue with how I'm clamping the camera rotation. How can I correct this? Or maybe I was on the right track and just missed something obvious?

Check out the project here:

This is my camera rotation function:

//Rotate camera horizontally
y -= mouseMove.x * 0.02;
//Rotate camera vertically
//Check if moving camera up or down
if (mouseMove.y < 0) {
    //Make sure it's not at top or bottom
    if (Math.abs(camera.rotation.y*(180/Math.PI)) > 2 || camera.rotation.x <= 0){
        x -= mouseMove.y * 0.02;
    }
} else if (mouseMove.y > 0) {
    if (Math.abs(camera.rotation.y*(180/Math.PI)) > 2 || camera.rotation.x >= 0){
        x -= mouseMove.y * 0.02;
    }
}

And here is my Update Camera function:

function updateCamera(){
    camera.lastRotation = camera.quaternion.clone();
    var euler = new THREE.Euler(0, 0, 0, 'YXZ');
    euler.x = x;
    euler.y = y;
    euler.z = z;
    mouseMove.x = 0;
    mouseMove.y = 0;
    camera.quaternion.setFromEuler(euler);
}

Here's an image illustrating the problem:

Answer №1

After encountering a long-standing issue with clamping cameras in ThreeJS for an fps-style camera, I decided to take matters into my own hands and devise a solution. Drawing inspiration from a helpful post on Stack Overflow, I was able to come up with the following code snippet:

// Ensure pointer lock is enabled for optimal camera responsiveness
const UP_AXIS = new THREE.Vector3(0, 1, 0);
let initialRotation = 0;

this.canvas.addEventListener("mousemove", (e) => {
    let newRotation = this.cameraRotation + e.movementY * -0.00001;
    // Implementing the clamping logic here
    newRotation = THREE.MathUtils.clamp(newRotation, -Math.PI / 2, Math.PI / 2);

    // Horizontal camera rotation without clamping
    this.camera.rotateOnWorldAxis(UP_AXIS, e.movementX * -0.0001 * settings.mouseSensitivity);

    // Camera rotation with clamping applied
    this.camera.rotateX(newRotation - initialRotation);

    initialRotation = newRotation;
})

I hope this workaround proves helpful to those facing similar challenges.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If the item already exists within the array, I aim to replace the existing object with the new one

I am faced with a situation where I have an array of objects, and when a user selects an option, it adds a new object to the array. My goal is to write a code that can check if this new object's key already exists in one of the objects within the arra ...

Is it possible to use the HTML script tag without specifying the type attribute as JavaScript? <script type="text/html"></script>?

While examining the source code of an HTML page, I stumbled upon the following snippet: <script id="searchItemTemplate" type="text/html"> <# var rows = Math.floor((Model.RecordsPerPage - 1) / 3 + 1); for (var i = 0; i < rows; ++i){ ...

The robots.txt file in Nuxt.js allows for multiple disallow directives for each user agent

With the Nuxt module called nuxt-robots, how can I set up multiple disallow rules per user agent? Currently, my configuration looks like this: robots: () => { return { UserAgent: '*', Disallow: '/search/', Si ...

Issues have been encountered with Angular 5 when trying to make required form fields work properly

Just created my first Angular app using Angular 5! Currently following the documentation at: https://angular.io/guide/form-validation. Below is the form I have set up: <form class="form-container"> <mat-form-field> <input matInput pl ...

Using setInterval to update the content of a Text Area continuously

I am currently working on a script that involves extracting a string from a textarea, breaking it down into an array using the delimiter "=====\n", and then displaying each element of the array in the textarea every 250ms. However, I have noticed that ...

Verify password criteria instantly

In my user registration form, users are required to enter a password that meets certain criteria such as containing 8 characters, numbers, and upper case letters. How can I indicate to the user if their password meets these criteria while they are typing? ...

Having trouble with e.preventDefault() not working on submit() in Javascript?

I'm facing an issue with submitting a form using JavaScript submit() LIVE ACTION : https://jsfiddle.net/98sm3f3t/ HTML : <form id="myForm" action=""> First name: <input type="text" name="fname"><br> <button id="myButton ...

How can we detect line breaks within a selection using JavaScript?

Apologies for the lack of expertise in the content below, as it is being produced by a designer experimenting with coding :D The goal here is to determine the number of lines selected or highlighted by the cursor. When I mention "lines," I mean what is vi ...

Is it possible to incorporate variables when updating an array or nested document in a mongodb operation?

Within the "myCollection" target collection, there exists a field named "japanese2". This field is an array or an object that contains another object with a property called "japanese2a", initially set to 0 but subject to change. My goal is to update this p ...

Is it possible to transfer elements from one array to another when clicked, but without copying the contents to the new array objects?

Welcome, For my latest project, I am excited to create a "Learning Cards" App from scratch. The concept is pretty straightforward: it consists of cards with questions. Upon clicking a button, you can reveal the correct answer. Additionally, there's a ...

How to eliminate the initial class with jQuery

I am encountering some issues with removing a class using jQuery. When I open a modal in Bootstrap, it generates code like this: <div class="modal-backdrop fade in"> in the footer. However, when I open a modal within another modal (2 modals), ther ...

What could be causing my Javascript element to continue moving past 200px?

My code has a "voila" element that is not stopping after reaching 200px. What could be the issue in the logic of the code and how can I fix it? var voila = document.querySelector(".voila"); voila.textContent = "hahahaha"; voila.style.position = "absolute" ...

Displaying the product quantity counter in the shopping cart immediately after adding the item

My website has a shopping cart quantity counter that doesn't update immediately when a product is added. Instead, it requires a page reload or navigation to another page for the change to be reflected. I would like the quantity counter to show the pro ...

Ensure that the folder name contains specific characters

When working with AngularJS, I am developing a feature to create folders. One requirement is that if a folder name contains special characters like /, :, ?, "<", or |, an error message should be displayed stating "A folder name cannot contain any of the ...

Ways to activate an event based on the dimensions (width/height) of

Exploring ways to implement an if statement based on specific width/height values using this code example. Check out the code snippet here My approach: <p id="confirmation">Try again!</p> <script> if (new dynamicSize.width() < ...

How can I retrieve the length of an array in vuejs?

This snippet includes a script tag <script> export default { data() { return { blogs: [], }; }, created() { this.paginate_total = this.blogs.length / this.paginate; }, }; </script> Displayed below is the respo ...

Why isn't ThreeJS camera.lookAt() working as expected? Am I missing something in my implementation?

When working with Three.js, my goal is to have a camera directed towards a specific point in 3D space. To achieve this, I attempted to use the camera.lookAt function in the following way: camera.lookAt(new THREE.Vector3(-100,-100,0)); Unfortunately, it ...

php and javascript

On my sales website, when I press the "sales" button, it opens a new frame. There is a chance for multiple frames to be open at the same time. My issue is that I want to close the frame after clicking enter and have the home page updated while keeping al ...

The number of characters can be measured in a div element that is editable

Looking to create a character counter similar to Twitter in a contenteditable div. The goal is to restrict users to typing a maximum of 140 characters. Using jQuery for character count and remaining display, but encountering an issue where the Enter key ( ...

The post method in Express.js is having difficulty parsing encoded data accurately

I'm currently working on an AngularJS code that sends a POST request like this: var req = { method: 'POST', url: 'http://localhost:3300/addInventoryItem', headers: { 'Content-Type': 'application/x-www-form- ...