I want to zoom the camera in Three/Aframe so an image fits to screen.
This is the code I'm using:
this._camera = document.getElementById('camera').getAttribute('camera')
this._ratio = this._assetWidth/this._assetHeight
this._vFOV = window.THREE.Math.degToRad( this._camera?.fov || 80 )
this._height = 2 * Math.tan( this._vFOV / 2 ) * this.data.distance
this._width = this._height * this._ratio
this._zoom = this._ratio > 1 ? this._width/window.innerWidth : this._height/window.innerHeight
console.log(this._zoom, this._ratio, this._width, window.innerWidth)
I've got to the part where I need to calculate Zoom so that the object fits to the screen, i.e. if it's landscape fits to width, if it's portrait fit to height.
I thought this was the answer but it's not. that's to calculate the camera position rather than zoom value.
I'm stuck on how you work out the zoom value.
Any clues?
Fitting an object to the screen by:
is quite similar once you understand where the formulas came from.
We'll use this neat image (from this SO thread) as it covers all three topics:
0. What do we want to achieve
We want the object (the longer side of either its width or height) to cover the filmHeight
- so it fits the screen.
1. Recalculating the FoV
In this case we do know the focalLength
(camera distance from the object) and filmHeight
(object width or height). We can calculate fov / 2
thanks to our friend trigonometry:
Tan (fov / 2) = (filmHeight / 2) / focalLength
=>
fov = 2 * ATan ((filmHeight / 2)) / focalLength * 180 / PI
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane")
const distance = this.el.object3D.position.distanceTo(plane.object3D.position)
var height = plane.getAttribute("geometry").height
var newFov = 2 * Math.atan((height / 2) / distance) * (180 / Math.PI); // in degrees
this.el.sceneEl.camera.fov = newFov
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>
2. Repositioning the object / camera
Same triangle different variables. Now we want to know the focalLength
:
Tan (fov / 2) = (filmHeight / 2) / focalLength
=>
focalLength = (filmHeight / 2) / Tan (fov / 2)
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane")
const height = plane.getAttribute("geometry").height
const fov = this.el.sceneEl.camera.fov * (Math.PI / 180);
const newDistance = Math.abs((height / 2) / Math.tan(fov / 2))
plane.object3D.position.z = -1 * newDistance;
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>
3. Zooming the camera
If we know what distance should the camera be from the object for it to fill the screen - we know what is the relation between the current distance, and the new one:
zoom = currentDistance / necessaryDistance
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane");
const distance = this.el.object3D.position.distanceTo(plane.object3D.position);
const height = plane.getAttribute("geometry").height;
const fov = this.el.sceneEl.camera.fov * (Math.PI / 180);
const newDistance = Math.abs((height / 2) / Math.tan(fov / 2));
this.el.sceneEl.camera.zoom = distance / newDistance;
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>