Hi. I have a plane geometry with large texture that I would like to fit in such a way as to cover the whole canvas. I’ve found various methods in some other threads, but either they fail to consider object size, or I am missing something (which is more likely).
I’ve managed to weave together a method to scale a plane based on image size (from R3F’s useAspect) + adjusting zoom (camera’s z coordinate) from this thread.
While canvas, plane and camera are certainly responsive, I’ve still failed to produce the desired result, which is:
if plane taller than it is wide (aspect < 1) and it’s wider than canvas, zoom out / decrease scale
if plane taller than it is wide (aspect < 1) and canvas is wider than plane, zoom in / increase scale
if plane wider than it is tall (aspect > 1) and it’s taller than canvas, zoom out / decrease scale
if plane taller than it is wide (aspect > 1) and canvas is taller than plane, zoom in / increase scale
In short, CSS object-fit: cover. I guess in my case FOV can also be used to achieve this (and keeping zoom constant), but so far this didn’t help.
If you don’t need this plane to be illuminated or to receive shadows, the simplest solution is probably to make a quad with custom shader material and do the math in NDC space.
I made this a while ago but abandoned it while losing my hair over the rotation issue with the “contain” mode. The plane should stay inside the field of view while rotating, but it doesn’t. Apart from that, it emulates CSS contain, fit, and cover pretty well.
Et voilà!
ps: Switch the camera to see both helper and main view mode.
There were a few issues. You were using the viewport size in your distance function, but you needed to use the object size (i.e. the image size). Also, the functions in my comment you linked use radians, not degrees!
I updated the comment to show a live codepen example.
Additionally, here’s your codesandbox updated and simplified with the corrections (try both “contains” and “cover” modes):
I was messing around with your code, trying to optimize, and I plugged the expanded expression for the tangent of half of vertical FoV (TAN_HALF_V_FOV) into Wolfram Alpha (I’m bad at math), and it gave a nice-looking optimization of the whole thing.
Instead of this:
Also, forgive my ignorance, but shouldn’t FoV (and tan of half of FoV, by extension) be recalculated on resize, since they are dependent on view height?
I’ve managed to cut down on a lot of calculations (including removing TAN_HALF_V_FOV altogether), as well as test my hypothesis regarding FoV recalculation. It all appears to work. Here’s the modified version, if anybody is interested: compassionate-fire-ys643m - CodeSandbox
Actually no, the vertical fov of the camera is constant in Three.js (unless you explicitly change it), and the output of the scene is dependent on both the vertical fov and the view aspect ratio (and not the vertical resolution).
This is why, when you resize any three.js scene vertically, it seems to always vertically fit the same amount content (but you will see more or less content of the left and right sides of the scene depending on if you shrink or grow the view heigh, respectively).
Take any default three.js example (not this special one we made), and shrink the height of the window so that the width is a lot larger than the height, and you’ll see what I mean: the content will shrink so that the same vertical space appears in the scene, but you will notice a lot more content is visible on the left and right sides of the scene.
Essentially Three.js always re-calculates the horizontal fov based on both the vertical fov and the aspect ratio of the view.
This is why in our particular example we had kept vfov constant, but were recalculating the hfov.