ThreeJS: Расчет FOV для перспективной камеры после изменения размера окна браузера

Мне нужно получить правильный FOV камеры Three.JS после изменения размера окна браузера. Я просмотрел следующие вопросы, но не могу найти ответ на свой вопрос:

Моя камера настроена так ("это" относится к объекту 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
);

Когда пользователь изменяет размер окна браузера, вызывается следующий код обновления. Я включил код, который нашел здесь: ( Как рассчитать fov для камеры Perspective за три секунды?), Чтобы попытаться вычислить новое 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 пикселей шире, я вижу гораздо большую ширину визуализированной трехмерной сцены. Представленное изображение, кажется, не искажает (то есть не более или менее "рыбий глаз"). Но значение camera.fov не изменяется (в журнале консоли я получаю "45" как FOV до и "45" как FOV после), и мой рассчитанный FOV совсем не корректен - со значением "6.33189...'.

Таким образом, мне кажется, что FOV используется для настройки projectionMatrix, но когда вызывается updateProjectionMatrix(), обратный расчет для обновления FOV не выполняется. Я использую THREE.JS r87 (редакция 87).

Вот исходный код, который я нашел по этой ссылке ( Как рассчитать fov для камеры Perspective в трех 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?

заранее спасибо

--- редактировать ----

Задав вопрос, я решил попытаться понять, смогу ли я это выяснить.

Я начал с отличной диаграммы от @rabbid76, найденной здесь: Расчет FOV FOV для PerspectiveCamera Я изменил изображение, чтобы показать вывод формулы для расчета 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.

- редактировать 2 -

@rabbid76 предложил правильный ответ в комментарии. Я хотел понять это, поэтому сделал несколько диаграмм, пока я обдумывал это:

Диаграмма показывает, что, поскольку плоскость области просмотра меняет размеры (h1 -> h2), можно рассчитать результирующее изменение в эффективном поле зрения (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 ответ

Решение

Если вы хотите знать, находится ли точка в области области просмотра, вам необходимо спроецировать эту точку на область просмотра.
использование 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).
(См. Транспонирование z-позиции от перспективы до ортогональной камеры в three.js)

Обратите внимание, что матрица проекции описывает отображение от трехмерных точек сцены к двухмерным точкам области просмотра. Матрица проекции преобразуется из пространства просмотра в пространство клипа, а координаты в пространстве клипа преобразуются в нормализованные координаты устройства (NDC) в диапазоне (-1, -1, -1) в (1, 1, 1) путем деления с компонентом w координат клипа.

Другие вопросы по тегам