I have a 3D scene I need to flatten towards the cammera in order for the geometry to still appear 3D when viewed in a 2D game from the front. I've done this in Maya with the shrinkwrap method and the "toward the world center" option but so far I've found no way to do this with Blenders shrinkwrap options or any other method in Blender. Is there a way to do this or with a pluggin?

In this example I used a torus and sphere to illustrate what I want but often I need more complex geometry that will not work with by the parallell projection methods Blender uses as it it then like the sphere would overlap the torus incorrectly from the origos point of perspective in flattened form.

enter image description here



enter image description here

Use the world to camera view method to map to image coordinates, similar to a UV with 0, 0 the bottom left hand corner, 1, 1 the top right hand corner.

The z component is mapped to 0 and the width scaled such that the projection image has width 1.

Using the half angle tangent of the camera angle, moving such that the width is one, maps the projection onto the rostrum (face or whatever it's called) of the camera.

>>> from bpy_extras.object_utils import world_to_camera_view
>>> world_to_camera_view(
world_to_camera_view(scene, obj, coord)
Returns the camera space coords for a 3d point.
(also known as: normalized device coordinates - NDC).
Where (0, 0) is the bottom left and (1, 1)
is the top right of the camera frame.
values outside 0-1 are also supported.
A negative 'z' value means the point is behind the camera.
Takes shift-x/y, lens angle and sensor size into account
as well as perspective/ortho projections.
:arg scene: Scene to use for frame size.
:type scene: :class:`bpy.types.Scene`
:arg obj: Camera object.
:type obj: :class:`bpy.types.Object`
:arg coord: World space location.
:type coord: :class:`mathutils.Vector`
:return: a vector where X and Y map to the view plane and
   Z is the depth on the view axis.
:rtype: :class:`mathutils.Vector`

To use the script below select whichever mesh objects you wish to project onto the scene camera plane.

import bpy

from bpy import context
from mathutils import Matrix, Vector
from math import tan, radians
from bpy_extras.object_utils import world_to_camera_view as w2cv

def tocam(scene, obs):
    cam = scene.camera
    cam_vec = cam.matrix_world.to_3x3() @ Vector((0, 0, -1))
    R = cam_vec.to_track_quat('-Z', 'Y').to_matrix().to_4x4()

    s = Vector((1, (scene.render.resolution_y / scene.render.resolution_x), 1, 1))
    # scale based on resolution
    S = Matrix.Diagonal(s)
    # translate such that origin is middle point of image (and hence cam)
    T = Matrix.Translation((-0.5, -0.5, 0))

    for ob in obs:
        ob.matrix_world = Matrix()
        for v in ob.data.vertices:
            vec = w2cv(scene, cam, v.co)
            v.co = vec.x, vec.y, 0

        ob.data.transform(S @ T)

        ob.matrix_world = R
        angle_x = cam.data.angle_x
        x = (0.5 /  tan(angle_x / 2)) * cam_vec.normalized()
        ob.matrix_world.translation = cam.matrix_world.translation + x
        if cam.data.type == 'ORTHO':
            ob.scale *= cam.data.ortho_scale

tocam(context.scene, context.selected_objects)

EDIT: fix for ortho camera.

  • $\begingroup$ When using your script, I get this error: TypeError: unsupported operand type(s) for @: 'Matrix' and 'Vector' $\endgroup$ – blenderNewbie Feb 4 '20 at 17:19
  • $\begingroup$ blender.stackexchange.com/questions/129473/… Clearly from screenshot this is for blender 2.8. $\endgroup$ – batFINGER Feb 5 '20 at 0:40
  • $\begingroup$ oh shoot. Could you give me a hint how to change the code, so it works for blender 2.79? $\endgroup$ – blenderNewbie Feb 5 '20 at 0:42
  • $\begingroup$ replace @ with * There is a similar script in question linked to in your recent question. $\endgroup$ – batFINGER Feb 5 '20 at 0:45
  • $\begingroup$ Thank you, this gives me another error about missing function Matrix.Diagonal(s). But when using the other script you mentioned (I guess this ) the coordinates I get make no sense at all. $\endgroup$ – blenderNewbie Feb 5 '20 at 13:36

This could be done with a script, but maybe an existing add-on will do?

If you install the shipped Tissue Tools add-on, you'll find it has a feature: 'UV to Mesh'.

So, having installed, you can set up a camera with the correct perspective view, and a square aperture. Then, while in the camera view, in Edit mode, U unwrap your objects, with the option 'Project from View'.

Then you can use the UV to Mesh option to convert the UV map into a mesh.

The new mesh is placed in the XY plane with UV 0 at World 0, 'attempting to match the surface area of the original object as closely as possible'

This may not suit, in which case a script may be preferable.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.