Framing a Maya camera depending on object size

Hello,

Within Maya, I’m trying to create a method to adjust a camera’s distance base on a objects size. This is to be able to render object thumb nail images whilst keep each render within frame regarding their size. I thought this could be achieved by finding an objects bounding box volume which is divided upon an average of another bounding box. This other bounding box has already been render correctly. Then using this average as a scale factor to the distance of the camera that rendered originally.

If there is an easy ways to do this please let me know.

If I am taking the correct approach, I would be using python maya cmds. How would I go about to achieve the following:

  1. Query a bounding box volume from an object.
  2. Would I need to only change the camera distance or change a camera’s attribute instead, such as focal length.

Thanks

I think the viewFit command is what you’re looking for.
viewFit Python Command | Maya Docs | Autodesk

1 Like

As a general rule, it’s always best to move the camera rather than change the focal length. The focal will greatly modify perspective.

Try filming a cube angle or a human face with a focal of 80, then 20: it will feel like different objects.

1 Like

I mean, zoom to fit is almost certainly the correct thing to use and there’s no real reason to derive an equation for this.

Absolutely no reason at all.

:grin:


So we can start with assuming your object has a height of 1 because the construction will scale with that height. Then we can draw a simple picture:

So we’ve got 2 right triangles, so we use the Pythagorean Theorem for those.
x^2 + d^2 = b^2
(1-x)^2 + d^2 = t^2

Then we’ve got a third triangle with one known angle, and a known side. That means the law of cosines.
b^2 + t^2 + 2*b*t*cos(a) = 1

Substitute the first two into the third:
(x^2 + d^2) + ((1-x)^2 + d^2) - 2 * sqrt(x^2 + d^2) * sqrt((1-x)^2 + d^2) * cos(a) = 1

And then, because computers are awesome, we can throw it at wolfram alpha!

And we get a nice simplified equation (4 possibilities because of the ± parts)
x = (1 ± sqrt(± 4d * cot(a) - 4d^2 + 1)) / 2

Finally, solve that for d (Also with wolfram alpha :slightly_smiling_face: )
d = (±cot(a) ± sqrt(cot^2(a) + 4 (1 - x) x)) / 2

So I didn’t actually test to see if this works, and I’m not sure what combination of the ± is correct, but there’s only 4 possibilities. Try 'em out!

Now you just set x to 0 if you want the camera even with the bottom, or 1 if you want it even with the top, and solve for the distance for a particular viewing angle.

Of course, you could keep going. Figure out what happens when x is out of the range (0, 1). Or maybe you could use a bounding cylinder instead of a box if you’re doing a turntable. Also, you’ve gotta handle width along with height. Also you’ve gotta figure out where to point the camera, but that one’s much more straightforward

4 Likes

Thanks everyone who replied. Especially @tfox_TD for a nice bit of maths! I kept it simple by using viewFit(). Thanks for that @MongoWobbler ! The modified perspective didn’t cause a problem with the thumbnail renders. Here is the code to position the camera:

    def set_persp_camera_position(fbx_name):


        # Render assets
       cmds.currentUnit(linear="meter")

        # Select object to frame within camera
        cmds.select(fbx_name)
        # set Perspective camera position
        cmds.xform("persp", ws=True, translation=(-3.885, 4.732, 12.531))
        cmds.xform("persp", ws=True, rotation=(-14.4, -23.6, 0.0))
        # Set camera focal length to 85
        cmds.setAttr("perspShape.focalLength", 85)
        cmds.viewFit()  # Frame camera view to fit object scale.
  1. I loved “no need for an equation, now buckle up!” :grin:

  2. Also worth noting, cmds.viewFit() has a fitFactor argument. It is a 0.0 to 1.0 flag. 1.0 is fully framed. 0.01 is zoomed way out. Anything less than 0.01 defaults to 1.0.

So if you need a bit of padding around the object, just make the fitFactor smaller.

You can also pass nodes, (or a list) into the function instead of selecting them.

Example, frame 2 cubes with a bit of padding around it:
cmds.viewFit('pCube1', 'pCube2', fitFactor=0.9)

cmds.viewFit(fbx_name, fitFactor=0.9)

1 Like