Three.js — Масштабирование плоскости на весь экран

#javascript #vector #three.js #glsl #shader

#javascript #вектор #three.js #glsl #шейдер

Вопрос:

Я добавляю плоскость к сцене следующим образом:

 // Camera
this.three.camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 60);
// Plane
const planeGeometry = new THREE.PlaneBufferGeometry(1,1,this.options.planeSegments,this.options.planeSegments);
const planeMat = new THREE.ShaderMaterial( ... )
this.three.plane = new THREE.Mesh(planeGeometry,planeMat);
this.three.scene.add(this.three.plane);
  

Довольно простой. Я пытаюсь выяснить, как мне нужно переместить плоскость по оси Z, чтобы она заполнила окно просмотра браузера. Для этого,

 // See attachment "solving for this" is closeZ
const closeZ = 0.5 / Math.tan((this.three.camera.fov/2.0) * Math.PI / 180.0);
this.uniforms.uZMax = new THREE.Uniform(this.three.camera.position.z - closeZ);
  

введите описание изображения здесь

Итак, теперь я знаю в своем шейдере, сколько я могу добавить к Z, чтобы плоскость заполнила окно просмотра. Вершинный шейдер выглядит следующим образом:

 uniform float uZMax;

void main() {   
    vec3 pos = (position.xy, uZMax);
    gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1 );
}
  

Это фактически увеличивает плоскость, чтобы заполнить окно просмотра, но по оси Y, а не по оси X.

введите описание изображения здесь

Я хотел бы выяснить, почему моя математика ссылается на ось Y и как мне нужно ее преобразовать, чтобы плоскость заполняла ширину окна просмотра вместо его высоты?

Редактировать:

Я пытаюсь добиться чего-то подобногоhttps://tympanus.net/Tutorials/GridToFullscreenAnimations/index4.html — Но в данном примере они просто масштабируют пиксели x и y, чтобы заполнить экран, и, следовательно, нет реального 3d — и, следовательно, снова не включается освещение.

Я хочу фактически переместить плоскость к камере, используя разные значения z, чтобы я мог вычислить нормали поверхности, чтобы затем снова рассчитать освещение во фрагментном шейдере по тому, насколько выровнена нормаль с направлением света — как это делается в raymarching.

Комментарии:

1. this.uniforms.uZMax = ... кажется, это должно быть this.uniforms.uZMax.value = ...

2. Действительно, если вы не используете new THREE.Uniform конструктор, это правда. Но класс three uniform заботится о добавлении объекта с правильными параметрами

3. ах, верно 🙂 мой плохой

Ответ №1:

Вы можете легко добиться такого эффекта полноэкранного режима, используя следующую настройку:

 const camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );

const geometry = new THREE.PlaneBufferGeometry( 2, 2 );
  

При создании сетки с этой геометрией и пользовательским шейдерным материалом ортографическая камера обеспечит желаемый эффект полноэкранного режима. Этот подход используется во всех примерах постобработки, где весь экран просмотра должен быть заполнен одним квадратом.

Комментарии:

1. Спасибо, но я на самом деле пытаюсь заменить щелкающие изображения плоскостью, а затем увеличить ее до полноэкранного режима, чтобы получить эффект сетки в полноэкранном режиме. Я мог бы попробовать какой-то поворот с помощью орфографического подхода. Тем не менее, мне было бы интересно узнать, как вместо этого связать математику с осью x. Есть идеи?

Ответ №2:

Я понял это, и, как подозревалось, это связано с соотношением сторон, передаваемым на камеру. Для тех, кто ищет решение после меня, вот как это работает:

Я ошибочно предположил, что поле значения для камеры одинаково во всех направлениях. Но поле обзора относится к полю обзора по оси Y, поэтому мы также должны преобразовать поле обзора камеры в ось x:

 function getXFOV() {
        // Convert angle to radiant
        const FOV = this.three.camera.fov;
        let yFovRadiant = FOV * Math.PI/180;
        // Calculate X-FOV Radiant
        let xFovRadiant = 2 * Math.atan( Math.tan(yFovRadiant/2) * (window.innerWidth / window.innerHeight));
        // Convert back to angle
        let xFovAngle = xFovRadiant * 180/Math.PI;
        return xFovAngle;

}
  

И затем мы просто используем этот угол в closeZ -вычислении вместо поля зрения камеры. Теперь она привязывается к ширине окна.

 const closeZ = 0.5 / Math.tan((this.getXFOV()) * Math.PI / 180.0);
this.uniforms.uZMax = new THREE.Uniform(this.three.camera.position.z - closeZ);