The example you’ve posted seems to be working with an image, you seem to be working with a mesh. They have the width and height available to them and can apply it to logic. You don’t have that, and only have height basically by setting camera.fov
.
What you need is something that relates to both that camera.fov
and your subject, you need to frame it somehow. You could use a bounding box, geometric checks and whatnot, but the easiest thing would be to just pick some world size rectangle, and apply the same logic you would to an image/quad.
So what is happening in your example?
Let’s say the image has an aspect ratio of 2:1. When you set your window to be 1000px x 500px it exactly aligns with the image, no overflow no gaps on either side. Cool, lets make the window narrower first, 500px.
So now at 500px x 500px we have overflow in the horizontal axis. This is cool and it’s how THREE.PerspectiveCamera
behaves “by default”. More on that in a second.
Let’s resize it to be wider than the image, 1500px x 500px. You’re doing that in your code and you experience what would be seen as a gap in the example demo. You’d see the entire image and 250px x 500px gaps on both sides.
Ok we don’t want that, so we need to do something to the image. We want the image to “fill the entire window”, so it needs to cover the 1500 x 500. The aspect ratio of that is 3:1, and the entire thing is 2:1. We want the 1000px of the image width to fit 1500px, so we will scale the image up by 1.5x. This gives us a height of 750px, and we can only fit 500px.
If the image was sitting on a mesh,
const image = new THREE.Mesh(new THREE.PlaneGeometry(1000,500))
And if you picked some constant fov
:
const myFov = 45
There is only one ever position of the camera that would render this mesh to fit a 1000px x 500px screen:
cam.position.z = heightHalf / Math.tan( THREE.Math.degToRad(myFov/2))
cam.position.x = cam.position.y = 0
Sweet. Now when we apply the usual resize logic:
cam.aspect = aspect
cam.updateProjectionMatrix()
And we resize the screen to 500px x 500px we get your result. But when we go to 1500px x 500px we see gaps.
We have:
500 x 500
1000 x 500
1500 x 500
Let’s divide all this with 500:
1x1
2x1
3x1
So the aspect of 1/2 is ideal and aligns with the image with no overflow or gaps. 1x1 has overflow, 3x1 has gaps. We like the behavior of < 1/2 but we don’t like the behavior of > 1/2.
How about this?
if(aspect < myAspect)
defaultCameraResize()
else
differentCameraResize()
The default behavior has already been mentioned above, we just compute the aspect and apply it to the camera. Since we don’t want to change the position of the camera, we need to change something else. The aspect always has to correspond to the viewport, so we still have to just compute that and pass it, otherwise our geometry would deform. How about we change the fov
?
renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight
const viewAspect = viewWidth / viewHeight
const special = camera.aspect > viewAspect
uniform.value = special ? 1 : 0
if(special){
const camH = Math.tan(THREE.Math.degToRad(myFov/2))
const ratio = camera.aspect / viewAspect
const newH = camH / ratio
const newFov = THREE.Math.radToDeg(Math.atan(newH)) * 2
camera.fov = newFov
} else {
camera.fov = myFov
}
camera.updateProjectionMatrix()
Fiddle:
https://jsfiddle.net/pailhead/qt5j2fbz/
So this is a generic implementation of that example. You can just find these numbers by trial and error based on your scene scale, or use some heuristic to compute it.