How to make Three.js FOV responsive to height AND width?
Asked Answered
M

1

7

Background

I size everything using viewport units:

body {
  font-size: calc((1vw + 1vh) / 2);
}

h6 { 
  font-size: 1em;
}

div { 
  width: 20em;
}

As the number of pixels in the screen increases, so will the size of units. For example, a 1920 x 1080 display will have smaller type than a 2560 x 1080 display. This allows automatic support of ultra-wide, vertical, and high DPI (8k or even 16K) displays without media queries.

Problem

Using Three.js, object scale responds only to the height of the screen. The object will appear the same size on a 1920x1080 monitor as it is on a 2560x1080 monitor. This is because Three.js camera uses a vertical field of view (FOV).

Default Behaviour - 2560x1080 compared to 1080x1080. Notice, the text becomes smaller, but the 3D object stays the same size. codepen example

2560x1080 1080x1080

My Attempt

The reason Three.js only responds to height is because it uses a vertical-fov. I tried to change the vertical-fov to diagonal-fov using this formula I found on stackOverflow here.

var height = window.innerHeight;
var width = window.innerWidth;
var distance = 1000;
var diag = Math.sqrt((height*height)+(width*width))
var fov = 2 * Math.atan((diag) / (2 * distance)) * (180 / Math.PI);
camera = new THREE.PerspectiveCamera(fov , width / height, 1, distance * 2);
camera.position.set(0, 0, distance);

The resulting behavior is the opposite of what should happen.

current results

Objects in Three.js only become larger when increasing the viewport height. I attempted to modify the default vertical-fov to a diagonal-fov. However, this did not work.

desired results

When the viewport is resized, the object should change perceived size based on the formula ((viewport height + viewport width) / 2). This will ensure and text placed on the page remains the same relative scale to 3D objects. I would like to achieve this by altering the camera, instead of the 3D objects themselves.

Mistreat answered 28/3, 2019 at 0:1 Comment(0)
P
0

You should create a scale ratio based on the width or height of the screen. For example:

SCREEN_WIDTH = window.innerWidth;
SCREEN_HEIGHT = window.innerHeight;

if(SCREEN_WIDTH > {something} && SCREEN_WIDTH < {something}){
    camera.fov = SCREEN_WIDTH / {something}; //This is your scale ratio.
};

//Repeat for window.innerHeight or SCREEN_HEIGHT.

if(SCREEN_HEIGHT > {something} && SCREEN_HEIGHT < {something}){
    camera.fov = SCREEN_HEIGHT / {something}; //This is your scale ratio for height, it could be same as window.innerWidth if you wanted.
};

//Updating SCREEN_WIDTH and SCREEN_HEIGHT as window resizes.

window.addEventListener('resize', onResize, false); //When window is resized, call onResize() function.

function onResize() {
    SCREEN_WIDTH = window.innerWidth; //Re-declaring variables so they are updated based on current sizes.
    SCREEN_HEIGHT = window.innerHeight;

    camera.aspect = window.innerWidth / window.innerHeight; //Camera aspect ratio.
    camera.updateProjectionMatrix(); //Updating the display
    renderer.setSize(window.innerWidth, window.innerHeight) //Setting the renderer to the height and width of the window.
};

Hope this helps! Let me know if you still have any questions

-Anayttal

Popelka answered 21/6, 2021 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.