When I apply "UV > Cube projection" it centers all the islands somewhere around (0.5,0.5) UV coordinates. I'd like, however, to preserve the the local vertex coordinates. For example if the vertex is located at (1,2,3), and is projected along the Z axis, then I want the UV coordinate of that vertex to become (1,2) rather than some arbitrarily translated value. Is it possible?

Rationale: I want the textures of different islands to be aligned with respect to each other.

EDIT: Example of cube projection (left) and desired result (right):

The center of the leftmost cube is located at (0,0,0) in this example.

  • 2
    $\begingroup$ 'Project from View' from an orthogonal view will preserve proportions.. but if you need object-space coordinates, see answer. $\endgroup$
    – Robin Betts
    Commented Aug 17, 2021 at 9:50
  • $\begingroup$ @RobinBetts: 'project from view' is what I did so far to work-around this issue. However it required manually selecting each set of faces (X, Y or Z-dominant), which is impractical for larger models. I actually don't care about object vs world space (could always bring the object to origin for that). Just need something more automatic. $\endgroup$ Commented Aug 17, 2021 at 14:31
  • $\begingroup$ Btw, I also tried the UV projection modifier with three orthogonal projectors, but it gave somewhat glitchy results. $\endgroup$ Commented Aug 17, 2021 at 14:47
  • 1
    $\begingroup$ Could you please clarify, eg edit in your comment on removed answer What is a Y dominant face, one with normal (0, 1, 0) ? Perhaps an image of desired result. $\endgroup$
    – batFINGER
    Commented Aug 17, 2021 at 14:53
  • $\begingroup$ @batFINGER I don't have enough rep to see the deleted answer. I added examples of the desired result (and my own solution in an answer). $\endgroup$ Commented Aug 19, 2021 at 0:42

2 Answers 2


Cube Projection.

In the python script templates there is a UV operator that sets the UV to the xy vertex coordinates.

Here is a minor edit to that

  • Made a dictionary of the three axes eg axes["x"] = (1, 0, 0)
  • For each face sort its keys by absolute value face normal dot axis.
  • The most aligned is last
  • Use first two coordinates to get coordinate
  • Note may want to test and flip for faces pointing opposite.

Template script with edits.

import bpy
import bmesh
from mathutils import Matrix

def main(context):
    obj = context.active_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)

    uv_layer = bm.loops.layers.uv.verify()
    axes = {a : vec for a, vec in zip("xyz", Matrix.Identity(3))}
    # adjust uv coordinates
    for face in bm.faces:
        axis = sorted(
            key=lambda a : abs(face.normal.dot(axes[a]))
        for loop in face.loops:
            loop_uv = loop[uv_layer]
            # use xy position of the vertex as a uv coordinate
            loop_uv.uv = getattr(loop.vert.co, "xyz".replace(axis, ""))


class UvOperator(bpy.types.Operator):
    """UV Operator description"""
    bl_idname = "uv.simple_operator"
    bl_label = "Simple UV Operator"

    def poll(cls, context):
        obj = context.active_object
        return obj and obj.type == 'MESH' and obj.mode == 'EDIT'

    def execute(self, context):
        return {'FINISHED'}

def register():

def unregister():

if __name__ == "__main__":

    # test call (requires edit mode)

Instead of building a UV map, I can calculate the UV with an OSL node with the following script:

shader cubemap(vector Position = P, vector Normal = N, output vector UV = 0.0) {
    vector p = Position;
    vector n = abs(Normal);
    if(n.x > n.y && n.x > n.z)
        UV = vector(p.y,p.z,0);
    else if(n.y > n.x && n.y > n.z)
        UV = vector(p.z,p.x,0);
        UV = vector(p.x,p.y,0);

Position and Normal can be connected to the corresponding inputs in the Texture Coordinate node to get local coordinates (or else it defaults to world coordinates).


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .