基于mayapy.exe的Maya .ma转Nuke .nk脚本开发:相机信息导出问题
把Maya相机导出为Nuke .nk工程的实现方案
嘿,我来帮你搞定把Maya相机导出成Nuke可用的.nk工程文件的问题!你之前用FBX导出的方式虽然能传递相机数据,但直接生成Nuke原生的.nk纯文本文件,不仅更高效,还能精准控制每一个相机参数和动画关键帧。
核心思路
Nuke的.nk文件本质是结构化的纯文本,我们只需要读取Maya相机的属性(静态参数+动画关键帧),然后按照Nuke Camera节点的语法格式生成文本,最后写入文件即可。
完整实现代码(mayapy批处理可用)
import maya.cmds as cmds import os import sys def export_maya_camera_to_nuke(): # 确保在批处理模式下也能正常初始化Maya if not cmds.about(batch=True): cmds.file(new=True, force=True) # 获取当前场景路径 current_scene = cmds.file(query=True, sceneName=True) if not current_scene: print("请先打开一个Maya场景文件!") sys.exit(1) # 获取选中的相机(如果没有选中则取场景主相机) selected_cameras = cmds.ls(selection=True, type='camera') if not selected_cameras: # fallback到场景主相机 selected_cameras = cmds.ls(type='camera') # 过滤掉默认的persp/top/front/side,取自定义相机或者persp selected_cameras = [cam for cam in selected_cameras if not cam in ['perspShape', 'topShape', 'frontShape', 'sideShape']] if not selected_cameras: selected_cameras = ['perspShape'] camera = selected_cameras[0] camera_transform = cmds.listRelatives(camera, parent=True)[0] print(f"正在处理相机:{camera_transform}") # ---------------------- 读取Maya相机静态属性 ---------------------- # 基础参数 focal_length = cmds.getAttr(f"{camera}.focalLength") horizontal_aperture = cmds.getAttr(f"{camera}.horizontalFilmAperture") vertical_aperture = cmds.getAttr(f"{camera}.verticalFilmAperture") near_clip = cmds.getAttr(f"{camera}.nearClipPlane") far_clip = cmds.getAttr(f"{camera}.farClipPlane") is_orthographic = cmds.getAttr(f"{camera}.orthographic") ortho_width = cmds.getAttr(f"{camera}.orthographicWidth") if is_orthographic else 0 # ---------------------- 读取动画关键帧数据 ---------------------- def get_keyframe_data(attr_path): """获取指定属性的关键帧(帧号+数值),返回[(frame, value), ...]""" keyframes = cmds.keyframe(attr_path, query=True, timeChange=True) values = cmds.keyframe(attr_path, query=True, valueChange=True) if keyframes and values: return list(zip(keyframes, values)) return None # 位移/旋转/缩放动画 tx_keys = get_keyframe_data(f"{camera_transform}.translateX") ty_keys = get_keyframe_data(f"{camera_transform}.translateY") tz_keys = get_keyframe_data(f"{camera_transform}.translateZ") rx_keys = get_keyframe_data(f"{camera_transform}.rotateX") ry_keys = get_keyframe_data(f"{camera_transform}.rotateY") rz_keys = get_keyframe_data(f"{camera_transform}.rotateZ") # 相机属性动画 focal_keys = get_keyframe_data(f"{camera}.focalLength") ortho_width_keys = get_keyframe_data(f"{camera}.orthographicWidth") if is_orthographic else None # ---------------------- 构建Nuke .nk内容 ---------------------- nk_content = f"""Camera {{ inputs 0 name "{camera_transform}" xpos 0 ypos 0 focal {focal_length:.6f} haperture {horizontal_aperture:.6f} vaperture {vertical_aperture:.6f} near {near_clip:.6f} far {far_clip:.6f} """ # 添加投影类型 if is_orthographic: nk_content += f" projection orthographic\n" nk_content += f" ortho_width {ortho_width:.6f}\n" else: nk_content += f" projection perspective\n" # 生成位移动画曲线 def add_animation_curve(nk_str, attr_name, key_data): if key_data: nk_str += f" {attr_name} {{\n" for frame, val in key_data: # Nuke曲线格式:curve 帧号 数值; (默认线性插值,如需匹配Maya插值可扩展) nk_str += f" curve {int(frame)} {val:.6f};\n" nk_str += f" }}\n" return nk_str nk_content = add_animation_curve(nk_content, "translate", tx_keys) nk_content = add_animation_curve(nk_content, "translate.y", ty_keys) nk_content = add_animation_curve(nk_content, "translate.z", tz_keys) nk_content = add_animation_curve(nk_content, "rotate", rx_keys) nk_content = add_animation_curve(nk_content, "rotate.y", ry_keys) nk_content = add_animation_curve(nk_content, "rotate.z", rz_keys) nk_content = add_animation_curve(nk_content, "focal", focal_keys) if is_orthographic and ortho_width_keys: nk_content = add_animation_curve(nk_content, "ortho_width", ortho_width_keys) # 闭合节点 nk_content += "}" # ---------------------- 写入.nk文件 ---------------------- output_filename = os.path.splitext(current_scene)[0] + '.nk' with open(output_filename, 'w') as f: f.write(nk_content) print(f"✅ 成功导出Nuke相机工程到:{output_filename}") if __name__ == "__main__": export_maya_camera_to_nuke()
关键注意事项
- 单位匹配:Maya和Nuke的相机光圈单位默认都是英寸,所以
horizontalFilmAperture可以直接复用;如果Maya用了其他单位(比如厘米),需要做单位转换(1英寸=2.54厘米)。 - 动画插值:上面的代码默认用线性插值,如果需要精确匹配Maya的关键帧插值类型(比如贝塞尔、缓入缓出),可以读取Maya关键帧的
inTangentType和outTangentType,然后转换成Nuke对应的插值语法(比如curve 0 10 bezier 0.5 0.5 0.5 0.5;)。 - 批处理模式适配:代码里加入了批处理模式的判断,确保用
mayapy.exe执行时能正常初始化Maya环境。 - 相机选择逻辑:如果没有选中相机,会自动 fallback 到场景中的自定义相机,最后才用默认的persp相机,避免报错。
内容的提问来源于stack exchange,提问作者William Eguienta




