Adjusting the field of view of a perspective camera in THREE.JS while maintaining the camera's original distance

My ultimate goal is to adjust the FOV value of my camera while triggering an animation. However, upon implementing the FOV value changes, I notice that my scene appears smaller.

This has led me to question the mathematical relationship between the FOV value and the distance position of a perspective camera.

The challenge lies in maintaining the same scene size (by adjusting the camera position) as the FOV value fluctuates.

Thank you for any insights or suggestions on this matter.

EDIT 1 :

Below is a code snippet depicting my dilemma: When I modify the FOV value of my camera (from 4 to 45), it alters the distance between my square and the camera. How can this be prevented?


                let W = window.innerWidth, H = window.innerHeight;

                let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( W, H );

                document.body.appendChild( renderer.domElement );

                let camera = new THREE.PerspectiveCamera( 4, W/H, 1, 100 );
                let scene = new THREE.Scene();

                camera.position.set(0,0,14);
                camera.lookAt(0,0,0);

                let geo = new THREE.BoxGeometry(0.5, 0.5, 0.5);
                let mat = new THREE.MeshNormalMaterial();
                let mesh = new THREE.Mesh(geo, mat);
                mesh.rotation.set(0.2,0.4,-0.1);
                scene.add(mesh);

                renderer.render(scene, camera);

                let progress = {};
                progress.fov = 4;


                TweenMax.to(progress, 2,{
                    fov:45,
                    onUpdate:function(){
                        camera.lookAt(0,0,0);
                        camera.updateProjectionMatrix();
                        camera.fov = progress.fov;

                        renderer.render(scene, camera);

                    },
                    repeat:-1,
                    ease:Power3.easeInOut
                });

            
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>

Answer №1

When a pinhole camera captures 3D points in the world, they are projected onto a 2D viewport in perspective projection, causing objects to appear smaller as their depth increases.
The relationship between the size of the projected area in view space and the Z coordinate is linear, determined by the field of view angle and aspect ratio.
For more information, refer to THREE.js PerspectiveCamera focalLength off by a factor of two, inconsistent with FOV.

https://i.sstatic.net/ic2rN.png

In perspective projection, the relationship between Z-distance and size for a given field of view (fov_y) can be calculated using the formula:

depth_s = Math.tan(fov_y/2.0 * Math.PI/180.0) * 2.0;

The size of an object on the viewport depends on the field of view and its depth. This results in objects appearing different when viewed from varying angles. To maintain consistent projection, one can define a distance where the object's size remains unchanged at that specific depth. However, the object's appearance will slightly vary before and after this defined distance. Refer to How to switch between Perspective and Orthographic cameras keeping size of desired object and Transpose z-position from perspective to orthographic camera in three.js.

Illustration: The visibility of the top point of a cube changes based on its distance from the camera.

https://i.sstatic.net/L0Vnw.png https://i.sstatic.net/A4quN.png

The initial field of view is 4.0, so the ratio between Z-distance and size is:

let init_depth_s    = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;

During an animation of the field of view, the current ratio between Z-distance and size becomes:

let current_depth_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;

To maintain the object's size, a new distance must be defined based on the reference distance (initially 14.0) scaled by the ratio init_depth_s / current_depth_s:

camera.position.set(0, 0, 14 * init_depth_s / current_depth_s);

An example scenario is provided below, modifying the original code (adjusting near plane to avoid clipping):

// Code snippet here...
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>

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

Obtain access to global.window.localStorage within getServerSideProps

I have a component that receives props with data and renders the data. In my functionality within the getServerSideProps, I am attempting to retrieve data from localStorage. However, due to window being undefined, I am unable to do so. I have tried using ...

JavaScript: Adding up whole numbers--- Reference Error: Undefined

I'm having trouble with my code because it's saying that "t1" is not defined, even though it's the name of my text box. I tried making the variable global by declaring it outside the function, but it didn't solve the issue. Interestingl ...

Enhancing images by creating clickable sections in a more organized and efficient manner

Are there any other methods to make parts of an image clickable in a more organized way, aside from using the coordinates method or directly embedding images from Photoshop? <script> new el_teacher = ["candice","john"]; $(".teacher1").mouseenter(fu ...

Switch the jQuery overlay image upon clicking a button

Is there a way to dynamically change the overlay image of a jQuery-ui dialog using a button click from inside the dialog? I attempted to do so in the code snippet below, but unfortunately, the overlay image does not update. It seems that I need to manipu ...

Performing AJAX callback function on success in jQuery

I've been trying to troubleshoot an error but none of the solutions I've found so far seem to be working for me. I have an ajax request set up to retrieve JSON data from the server. I can see the JSON data that I want to capture when using consol ...

When you try to import from another file, the method is not defined

When I attempt to import a method from another file, I am encountering an issue where it returns undefined. The structure involves 3 files with one calling the next in sequence. File1: const { methodFromFile2 } = require('./file2'); methodFromFi ...

In TypeScript, the function is failing to retrieve the complete array value

I'm facing an issue with a program that is supposed to piece together all the letters, but it's not functioning correctly. I've tried using the debugger, but it doesn't display any errors. Here's the code snippet: var phrase = [ &a ...

How can I prevent an HTML element from losing focus?

Currently, I am developing an online editor that requires accurate character inputs. To achieve this, I have implemented the jQuery.onKeyPress event on a textarea element. This approach was chosen because obtaining character inputs from the body directly p ...

React is having trouble resolving the path required

Having an issue with the tracking-js library in my project. I'm using React and despite checking my package.json and confirming that the module is installed, I keep receiving errors indicating that the module cannot be found. This is how I am attempti ...

Check if array2 is partially ordered as array1 in JavaScript

Is there a more efficient way to achieve these conditions in JavaScript using vanilla or lodash? const array1 = [1, 2] const array2 = [1, 2, 3, 4] //true const array2 = [1, 3, 2, 4] //true const array2 = [4, 3, 2, 1] //false const array2 = [2, 3, 1, 4] / ...

unable to effectively test promises with stubs using nodejs mocha

Currently, I am facing a couple of challenges when attempting to perform unit testing on promises using Mocha. 1) The main issue I encounter is an Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Prom ...

Store the output of JavaScript in a PHP variable

My objective is to convert any image to base 64 and then store the converted string in a PHP variable before inserting it into my database. JavaScript Code: function uploadFile() { if (this.files && this.files[0]) { var FR= new FileReader ...

Hide the drawer when a user clicks on a currently selected tab

Just starting to explore Material-UI and React here, so please bear with me if I'm making any rookie mistakes. :) I've set up a Drawer component containing a Tabs element with multiple Tab components inside. It's nothing too fancy yet - mos ...

The window resizing function continues on an endless loop

I could really use some assistance in pinpointing the issue at hand! My script is set up to display a banner at the top of a page only if the window width is 600px or less and the scroll position is within 34px from the top. It hides the banner when the u ...

A Nuxt plugin that integrates a separate website into the serverMiddleware

The concept Imagine having a main Nuxt website just like any other. Now, think about adding my module to your project. This module will then introduce a subdomain "admin.example.com" to your project, creating a fully functional Nuxt-based website that ope ...

Optimizing Static File Caching in Yii

Having a frustrating issue with Yii where my local development environment caches CSS and JS files. Despite making changes to the file, the edits do not reflect in the output and sometimes causes corruption leading to broken functionality. This problem see ...

Issues with router middleware functionality in Node.js hinder proper operations

Currently, I am working with Nodejs and utilizing "Express js". One of the tasks at hand involves implementing a "Router Middleware" function. With my current code setup, whenever I access "http://localhost:3000", the "router middle" functionality is tri ...

Simple steps for calling a lit component in React using the ScopedElementsMixin:1. Import

Looking to incorporate the web component Button (lit) into my project using a similar tag approach. For instance, if the <button-test> tag is present on the website, then it should be converted to <button-test-12345>. This is why ScopedElements ...

Transforming Arrays of Objects into a Single Object (Using ES6 Syntax)

My attempt to create aesthetically pleasing Objects of arrays resulted in something unexpected. { "label": [ "Instagram" ], "value": [ "@username" ] } How can I transform it into the d ...

Error message: "An undefined index error occurred during an Ajax call to a

Path: homepage -> initiate ajax request to tester.php on PHP -> receive JSON data back to homepage. I am struggling to resolve this issue. Any help would be appreciated. AJAX Request: $.ajax({ url : "tester.php", ty ...