# Perspective Flatten Geometry by Shrinkwrap

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.

bpy_extras.object_utils.world_to_camera_view

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 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.data.transform(ob.matrix_world)
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

bpy.ops.object.duplicate()
tocam(context.scene, context.selected_objects)


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.