I need to obtain the correct Three.JS camera FOV after the browser window size has changed. I have viewed the following questions, but can't seem to find an answer for my question:
- How to calculate fov for the Perspective camera in three js?
- Calculating frustum FOV for a PerspectiveCamera
My camera is set up like this ('this' refers to a gameCamera object I setup):
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 browser window is resized by the user, the following update code is called. I included code I found here: (How to calculate fov for the Perspective camera in three js?) for trying 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()
But it doesn't seem to work. After the window is resized, for example dragging it 500 pixels wider, I can see a much greater width of the rendered 3D scene. The rendered view doesn't seem to distort (i.e. no more or less 'fisheye' appearance). But the camera.fov value doesn't change (in the console log, I get '45' as FOV before, and '45' as FOV afterwards) and my calculated FOV is not at all correct -- with a value of '6.33189...'.
Thus it seems to me that FOV is used to set up the projectionMatrix, but when updateProjectionMatrix() is called, the reverse calculation is not done to update FOV. I am using THREE.JS r87 (revision 87).
Here is the original code I found at this (How to calculate fov for the Perspective camera in three js?) link for calculating FOV:
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);
The comments at that link seem to indicate that the formula is correct.
Questions:
- Am I correct that camera.updateProspectiveMatrix() doesn't change the .fov property?
- Is my understanding that the FOV changes when the screen gets wider correct? or am I somehow confused about what FOV means?
- What am I doing wrong? How do I correctly calculate the camera FOV?
Thanks in advance
--- edit ----
After asking the question, I decided to try to see if I could figure this out.
I started with the excellent diagram from @rabbid76 found here: Calculating frustum FOV for a PerspectiveCamera I modified the image to show derivation of formula for calculating FOV if one knows the far plane dimensions and the camera distance.
I understand now the derivation of the formula. And I see that if given a starting vertical FOV, then I can calculate my far plane width and height as follows:
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;
But I'm still stuck. I don't understand the correlation between the rendering window size (i.e. the browser window) and the far plane size. If my cameraDist is huge (e.g. 1,000,000), my far plane will also be huge.
I assume my rendering window is somewhere between the near plane and the far plane. So what I need is the distance to the rendering plane.
I still can't figure out how to determine the new camera FOV after changes to window.innerHeight or window.innerWidth.
-- edit 2 --
@rabbid76 suggested the correct answer in a comment. I wanted to understand it, so made some diagrams while I considered this:
What the diagram shows is that as the viewport plane changes sizes (h1 --> h2), one can calculate the resulting change in effective field of view (FOV).
If the viewport is not square, then one can also use this formula to calculate the horizontal FOV if the vertical FOV is known and the aspect ratio is known.
To put this all for a final answer, 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()
camera.fov
is the vertical FOV -- not the horizontal FOV. See #17838152. – Bekki