After changing the size of the browser window, I'm trying to determine the correct Three.JS camera FOV. I have looked at several related questions, but haven't found a solution yet:
- How to calculate fov for the Perspective camera in three js?
- Calculating frustum FOV for a PerspectiveCamera
This is how my camera is configured ('this' refers to a gameCamera object I created):
const CAMERA_DIST = 8000;
-other stuff-
this.camera = new THREE.PerspectiveCamera(
45, //FOV parameter
window.innerWidth / window.innerHeight, //aspect ratio parameter
1, //frustum near plane parameter
CAMERA_DIST //frustum far plane parameter
);
When the user resizes the window, the following update code is executed. I tried using code from this link (How to calculate fov for the Perspective camera in three js?) to calculate a new FOV ('aFOV').
function onWindowResize() {
gameCamera.camera.aspect = window.innerWidth / window.innerHeight;
console.log(gameCamera.camera.fov);
gameCamera.camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
console.log(gameCamera.camera.fov);
let aFOV = 2*Math.atan((window.innerHeight)/(2*CAMERA_DIST)) * (180/Pi);
console.log(aFOV);
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
} //onWindowResize()
However, it doesn't seem to be working properly. When resizing the window by, for example, dragging it wider by 500 pixels, the rendered 3D scene appears much wider. The FOV value remains unchanged (both before and after resize) and the calculated FOV ('6.33189...') is incorrect.
It appears that the FOV is used to set up the projectionMatrix, but when updateProjectionMatrix() is called, the reverse calculation to update FOV doesn't happen. I am using THREE.JS r87 (revision 87).
The original code I found for calculating FOV can be seen at this link (How to calculate fov for the Perspective camera in three js?):
var height = 500;
var distance = 1000;
var fov = 2 * Math.atan((height) / (2 * distance)) * (180 / Math.PI);
itsLeftCamera = new THREE.PerspectiveCamera(fov , 400 / 500, 1.0, 1000);
Now, I have the following questions:
- Does camera.updateProspectiveMatrix() not change the .fov property?
- Is my understanding correct that FOV changes as the screen gets wider? Or am I misunderstanding what FOV means?
- What am I missing here? How can I accurately calculate the camera FOV?
Thank you in advance.
--- edit ----
After posting my question, I decided to try and figure it out myself.
I referred to a helpful diagram by @rabbid76 found here: Calculating frustum FOV for a PerspectiveCamera. I modified the image to illustrate the derivation of a formula for calculating FOV based on far plane dimensions and camera distance.
https://i.sstatic.net/ev2ay.png
I now understand the formula derivation and can calculate far plane width and height if given an initial vertical FOV:
this.cameraDist = CAMERA_DIST;
this.aspectRatio = window.innerWidth / window.innerHeight;
this.farPlaneHeight = Math.tan(this.vertFOV/2) * this.cameraDist;
this.farPlaneWidth = this.Height * this.aspectRatio;
However, I still need to determine the relationship between the rendering window size (browser window) and the far plane size. If my cameraDist is large (e.g., 1,000,000), the far plane will also be huge.
I assume the rendering window lies between the near and far planes. So, I need to find the distance to the rendering plane.
I'm still struggling with figuring out how to calculate the new camera FOV after changes to window.innerHeight or window.innerWidth.
-- edit 2 --
@rabbid76 provided the correct answer in a comment. I made some diagrams to help me understand it:
https://i.sstatic.net/cCikB.png
The diagram shows how changes in the viewport plane sizes (h1 --> h2) can affect the effective field of view (FOV). It also provides a formula to calculate horizontal FOV based on known vertical FOV and aspect ratio when the viewport is not square.
To summarize, I use the following code:
//Below is code used when initializing the perspective camera
this.viewportWidth = window.innerWidth;
this.viewportHeight = window.innerHeight;
this.aspectRatio = window.innerWidth / window.innerHeight;
this.vertFOV = params.FOV || CAMERA_FOV
this.horizFOV = this.calculateHorizFOV();
this.camera = new THREE.PerspectiveCamera(this.vertFOV, this.aspectRatio, 1, this.cameraDist);
...
calculateHorizFOV() {
let radVertFOV = this.vertFOV * Pi/180;
let radHhorizFOV = 2 * Math.atan( Math.tan(radVertFOV/2) * this.aspectRatio);
let horizFOV = radHorizFOV * 180/Pi;
return horizFOV;
}
Then, when the user resizes the screen, I use this code:
function onWindowResize() {
let oldHeight = gameCamera.viewportHeight;
let oldWidth = gameCamera.viewportWidth;
let newHeight = window.innerHeight;
let newWidth = window.innerWidth;
gameCamera.viewportHeight = newHeight;
gameCamera.viewportWidth = newWidth;
gameCamera.aspectRatio = newWidth / newHeight;
let oldRadFOV = gameCamera.vertFOV * Pi/180;
let newRadVertFOV = 2*Math.atan( Math.tan(oldRadFOV/2) * newHeight/oldHeight);
gameCamera.vertFOV = newRadVertFOV * 180/Pi;
gameCamera.calculateHorizFOV();
gameCamera.camera.aspect = gameCamera.aspectRatio;
gameCamera.camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
} //onWindowResize()