ThreeJS:在浏览器窗口调整大小后计算透视摄像机的FOV

时间:2017-11-08 16:01:45

标签: 3d three.js perspectivecamera

我需要在浏览器窗口大小改变后获得正确的Three.JS相机FOV。我查看了以下问题,但似乎无法找到我的问题的答案:

我的相机设置如下('this'指的是我设置的gameCamera对象):

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
);

当用户调整浏览器窗口大小时,将调用以下更新代码。我在此处找到了代码:(How to calculate fov for the Perspective camera in three js?),用于尝试计算新的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()

但它似乎不起作用。调整窗口大小后,例如将其拖动500像素更宽,我可以看到渲染的3D场景的宽度更大。渲染的视图似乎没有扭曲(即没有或多或少的'鱼眼'外观)。但是camera.fov值没有改变(在控制台日志中,我得到'45'作为FOV之前,'45'作为FOV之后)和我计算的FOV完全没有正确 - 值为'6.33189 ......“。

因此在我看来,FOV用于设置projectionMatrix,但是当调用updateProjectionMatrix()时,不会进行反向计算来更新FOV。我正在使用THREE.JS r87(修订版87)。

以下是我在此(How to calculate fov for the Perspective camera in three js?)链接中找到的用于计算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);

该链接的评论似乎表明该公式是正确的。

问题:

  • 我是否更正了camera.updateProspectiveMatrix()不会更改.fov属性?
  • 我的理解是,当屏幕变宽时,FOV会发生变化吗?或者我对FOV的含义感到困惑?
  • 我做错了什么?如何正确计算相机FOV?

提前致谢

---编辑----

在问了这个问题之后,我决定试着看看能否解决这个问题。

我从@ rabbid76的优秀图表开始:Calculating frustum FOV for a PerspectiveCamera我修改了图像以显示计算FOV的公式的推导,如果你知道远平面尺寸和相机距离。

enter image description here

我现在理解公式的推导。我看到如果给出一个起始垂直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;

但我仍然被卡住了。我不理解渲染窗口大小(即浏览器窗口)和远平面大小之间的相关性。如果我的cameraDist很大(例如1,000,000),我的远程飞机也将是巨大的。

我认为我的渲染窗口位于近平面和远平面之间。所以我需要的是到渲染平面的距离。

在更改window.innerHeight或window.innerWidth之后,我仍然无法弄清楚如何确定新的相机FOV。

- 编辑2 -

@ rabbid76在评论中提出了正确答案。我想了解它,所以在我考虑这个时制作了一些图表:

enter image description here

图表显示的是,当视口平面改变大小(h1 - > h2)时,可以计算有效视野(FOV)的结果变化。

如果视口不是方形,那么如果已知垂直FOV并且已知纵横比,则也可以使用此公式计算水平FOV。

为了得到最终答案,我使用以下代码:

//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;
} 

然后,当用户调整屏幕大小时,我使用此代码。

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()

1 个答案:

答案 0 :(得分:1)

如果您想知道,如果某个点位于视口区域中,则必须在视口上投影该点。
请使用Vector3.project

camera   = THREE.PerspectiveCamera
pt_world = Three.Vector3

pt_ndc = new THREE.Vector3()
pt_ndc.copy(pt_world).project(camera)

如果结果位于范围(-1,-1,-1)到(1,1,1)范围内的规范化设备空间中,则该点在视口上。
(见Transpose z-position from perspective to orthogonal camera in three.js

注意,投影矩阵描述了从场景的3D点到视口的2D点的映射。投影矩阵从视图空间转换到剪辑空间,剪辑空间中的坐标转换为范围(-1,-1,-1)到(1,1,1)范围内的规范化设备坐标(NDC)通过用剪辑坐标的w分量来划分。

enter image description here