我需要在浏览器窗口大小改变后获得正确的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);
该链接的评论似乎表明该公式是正确的。
问题:
提前致谢
---编辑----
在问了这个问题之后,我决定试着看看能否解决这个问题。
我从@ rabbid76的优秀图表开始:Calculating frustum FOV for a PerspectiveCamera我修改了图像以显示计算FOV的公式的推导,如果你知道远平面尺寸和相机距离。
我现在理解公式的推导。我看到如果给出一个起始垂直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在评论中提出了正确答案。我想了解它,所以在我考虑这个时制作了一些图表:
图表显示的是,当视口平面改变大小(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()
答案 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分量来划分。