You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Maya如何获取设置世界空间顶点法线,解决烘焙自定义枢轴法线错乱问题

Maya烘焙自定义枢轴时保持自定义法线不变的问题

我正在开发一款用于烘焙自定义枢轴的小型工具,本身功能并不特殊。但在使用中我发现,执行bakeCustomPivot命令时会破坏自定义法线,而在我的需求中,法线朝向必须全程保持不变。我最初想到的方案是在烘焙枢轴前保存顶点法线,待烘焙完成后恢复法线,但实际操作时遇到了问题:

调用cmds.polyNormalPerVertex( q=1, normalX=1)查询到的法线值,在枢轴烘焙前后返回结果完全一致,这是因为法线朝向会和对象枢轴朝向同步变换,因此局部空间下的法线数值不会发生变化。我也曾尝试先计算枢轴烘焙前后的朝向差值,再将差值叠加到法线数值上,也没有获得预期效果。
经过梳理我打算更换实现思路:直接获取世界空间下的顶点法线数值,这样就不会受局部枢轴变换的影响,但目前我没有找到对应的实现方法。

你可以运行下方测试代码更清晰地复现问题:

import maya.cmds as cmds
import maya.mel as mel

# create test plane
plane = cmds. polyPlane(axis=[0,0,1], subdivisionsHeight=2, subdivisionsWidth=2)
cmds.select(plane[0]+'.vtx[*]')
objVertices = cmds.ls(sl=1,fl=1)


# Activate normals preview
cmds.select(plane)
mel.eval('ToggleVertexNormalDisplay')

# Change plane normals to any
cmds.polyNormalPerVertex(objVertices, normalXYZ=[0.3, 0.4, 1.0])

# get vtx normal vector for one vtx before bake
cmds.select(objVertices[0])
oldVtxNormalX = cmds.polyNormalPerVertex( q=1, normalX=1)
oldVtxNormalY = cmds.polyNormalPerVertex( q=1, normalY=1)
oldVtxNormalZ = cmds.polyNormalPerVertex( q=1, normalZ=1)
oldVtxValue = list(map(lambda x: x[0], [oldVtxNormalX] + [oldVtxNormalY] + [oldVtxNormalZ]))

# Warning. Why values are differ from custom values? They are smaller
print ( '{} --------- Old normal value '.format(oldVtxValue) ) # [0.26832816004753113, 0.3577708899974823, 0.8944271802902222]


# Rotate pivot, bake it (Modify/Bake Pivot)
cmds.select(plane)
cmds.manipPivot(o=[45.0, 0.0, 0.0])  # apply pivot
mel.eval('BakeCustomPivot')


# get vtx normal vector for one vtx after bake
cmds.select(objVertices[0])
newVtxNormalX = cmds.polyNormalPerVertex( q=1, normalX=1)
newVtxNormalY = cmds.polyNormalPerVertex( q=1, normalY=1)
newVtxNormalZ = cmds.polyNormalPerVertex( q=1, normalZ=1)
newVtxValue = list(map(lambda x: x[0], [newVtxNormalX] + [newVtxNormalY] + [newVtxNormalZ]))
print ( '{} --------- New normal value after bake. Same result '.format(newVtxValue) ) # [0.26832816004753113, 0.3577708899974823, 0.8944271802902222]

# !!!! As you can see normal values still same despite of fact pivot have changed

解决方法

核心原理

polyNormalPerVertex默认返回的是局部空间下的法线值,所以枢轴变换后局部值不变但世界朝向已经偏移。要固定世界空间下的法线朝向,只需要做两步坐标空间转换:

  1. 烘焙枢轴前,将每个顶点的局部法线转换为世界空间法线并存储
  2. 烘焙枢轴完成后,将存储的世界空间法线转换为新的局部空间,再写回顶点

具体实现代码

你可以在原有逻辑的基础上加入空间转换的逻辑,示例代码如下:

import maya.cmds as cmds
import maya.mel as mel
import maya.api.OpenMaya as om

def get_world_normals(mesh_name):
    sel = om.MSelectionList()
    sel.add(mesh_name)
    mesh_dag = sel.getDagPath(0)
    mesh_fn = om.MFnMesh(mesh_dag)
    # 直接获取世界空间下的所有顶点法线
    normals = mesh_fn.getNormals(om.MSpace.kWorld)
    return [ (n.x, n.y, n.z) for n in normals ]

def set_normals_from_world(mesh_name, world_normals):
    sel = om.MSelectionList()
    sel.add(mesh_name)
    mesh_dag = sel.getDagPath(0)
    mesh_fn = om.MFnMesh(mesh_dag)
    # 把世界空间法线转成当前局部空间再写回
    local_normals = []
    # 法线空间转换需要使用世界矩阵的逆转置矩阵
    world_inverse_transpose = mesh_dag.inclusiveMatrixInverse().transpose()
    for n in world_normals:
        world_normal = om.MVector(n)
        local_normal = world_normal * world_inverse_transpose
        local_normal.normalize()
        local_normals.append(local_normal)
    mesh_fn.setNormals(local_normals, om.MSpace.kObject)

# 原有业务逻辑
plane = cmds.polyPlane(axis=[0,0,1], subdivisionsHeight=2, subdivisionsWidth=2)[0]
cmds.select(plane+'.vtx[*]')
objVertices = cmds.ls(sl=1,fl=1)
mel.eval('ToggleVertexNormalDisplay')
cmds.polyNormalPerVertex(objVertices, normalXYZ=[0.3, 0.4, 1.0])

# 步骤1:烘焙前保存世界空间法线
pre_bake_world_normals = get_world_normals(plane)

# 执行枢轴烘焙
cmds.select(plane)
cmds.manipPivot(o=[45.0, 0.0, 0.0])
mel.eval('BakeCustomPivot')

# 步骤2:烘焙后恢复世界空间法线
set_normals_from_world(plane, pre_bake_world_normals)

注意事项

  • 这里用到了Maya的OpenMaya API来处理空间转换,比纯用cmds手动计算矩阵更高效准确
  • 写入法线前做了归一化处理,避免法线长度异常导致的渲染问题
  • 支持任意枢轴变换(平移、旋转、缩放)后的法线保持,不需要单独计算枢轴的变换差值

内容的提问来源于stack exchange,提问作者Andrew Salov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 04:15:04