【maya】在maya中对物体创建locator定位器做跟踪
2026/8/18 14:18:38 网站建设 项目流程

用途:灯光跟随 or 对角色某个顶点跟踪

1、创建

locator又名定位器,有多种创建方式,可以在创建下拉菜单中创建,也可直接在绑定面板创建

方法1
方法2

2、maya使用情况(一)

有一个情景,角色在跑步和跳跃,需要被聚光灯持续照射。(对应角色在不停的运动,灯光需要对角色跟踪的情况。)

第一步,首先是创建locator,随后将locator按住X吸附到物体身上,选择子物体(定位器) → 再选父物体(角色) → 按P,绑定父子级关系。这一步是让locator跟着角色运动

第二步,创建 SpotLight,选择物体下的locator,再选择需要被约束SpotLight做一个目标约束

层级关系如下:让Locator随着角色运动而运动了,灯光始终对准角色不改变。

物体移动

带动Locator移动

Spot Light跟随旋转运动

灯光跟随物体

甚至可以在目标约束的同时,套娃一个父子约束,让灯光不单单固定在原点对角色追踪,像是把一个灯光绑定在摇臂上,始终跟拍随角色运动

locator的作用:由于在项目中经常使用引用物体的abc,因此物体是不允许改变的运动,locator就起代替的作用,例如希望对物体进行一个追光/扫光,最后离开角色,直接对locator进行动画的K帧,就能较为轻易控制追光出现的位置。又或者比如灯光追踪目标晃动感,直接给locator些表达式rotateY = noise(time*0.5)*0.5;就可以使用上了。(具体maya表达式怎么写,直接问AI即可)

3、maya导出locator在nuke中使用

一个情景是,我需要把一个定位器放在角色身上某个位置,例如角色带结算的耳环,角色牙齿上,角色手环,这种角度刁钻的位置,选择物体顶点,创建一个铆钉,会在角色身上生成一个始终跟随的定位器(这个定位器生成跟随与角色顶点UV有关,顶点UV错误情况下定位器也会有bug)

角色顶点定位器跟随的效果

创建铆钉会发现虽然定位器可以始终跟随在角色身上,但是pinOutput定位器的位移数值始终是0,即使导出abc也没有正确的数值。

解决方式是自己创建一个locator,使用父子约束或者是点约束,取消偏移让新的locator获取到pinOutput的位移数值

选择locator烘焙关键帧,再正常导出abc即可得到一个带位移的locator

最后再nuke中按R选择路径下的locator.abc导入,或者创建Axis读取

还有一种办法是用Reconcile3D节点,将三维的数据转为2d平面的跟踪数据,这样好处是对一些比较刁钻的区域免去了手Kroto麻烦

注意需要在copy时,需减去原来的数值
我将创建locator功能导出abc用ai快速写一个maya脚本
# -*- coding: utf-8 -*- """ Maya 2022 Rivet -> Locator -> Bake -> Alembic Tool 工作流: 1. 选择模型上的 Vertex / Edge / Face 2. Create Rivet Locator 3. Maya 创建 uvPin + pinOutput 4. 创建新的 Locator 5. pinOutput -> Parent Constraint -> Locator 6. Bake Locator 7. 删除约束和临时 Rivet 8. 可选择导出 Locator 为 Alembic Author: GPT """ import maya.cmds as cmds import maya.mel as mel import os WINDOW = "rivet_locator_tool" # --------------------------------------------------------- # 工具函数 # --------------------------------------------------------- def get_selected_components(): """获取当前选择的模型组件""" sel = cmds.ls(sl=True, fl=True) if not sel: return [] components = [] for item in sel: if "." in item: components.append(item) return components def get_pin_outputs(): """ Rivet 执行后 Maya 会选择: uvPin + pinOutput 找出其中的 pinOutput。 """ sel = cmds.ls(sl=True, type="transform") outputs = [] for obj in sel: if cmds.objExists(obj): # Maya Rivet 创建出来的输出 locator if "pinOutput" in obj: outputs.append(obj) return outputs def get_uvpin_nodes(): """获取当前场景中所有 uvPin""" return cmds.ls(type="uvPin") or [] def get_new_uvpin(before): """找出 Rivet 新创建的 uvPin""" after = get_uvpin_nodes() for node in after: if node not in before: return node return None # --------------------------------------------------------- # 创建 Rivet # --------------------------------------------------------- def create_rivet_locator(*args): """ 选择 Vertex / Edge / Face 后: Component ↓ cmds.Rivet() ↓ uvPin ↓ pinOutput ↓ Parent Constraint ↓ Locator """ components = get_selected_components() if not components: cmds.warning( "请先选择模型上的 Vertex / Edge / Face。" ) return # 检查是否来自同一个模型 meshes = [] for comp in components: mesh = comp.split(".")[0] if mesh not in meshes: meshes.append(mesh) if len(meshes) != 1: cmds.warning( "一次操作请选择同一个模型上的组件。" ) return # 保存当前选择 original_selection = list(components) # 记录创建前的 uvPin before_uvpin = get_uvpin_nodes() try: # ------------------------------------------------- # Maya 原生 Rivet # ------------------------------------------------- cmds.select(original_selection, r=True) cmds.Rivet() except Exception as e: cmds.warning( "Rivet 创建失败:{}".format(e) ) return # ----------------------------------------------------- # 找新创建的 uvPin # ----------------------------------------------------- uvpin = get_new_uvpin(before_uvpin) # ----------------------------------------------------- # 找 pinOutput # ----------------------------------------------------- rivet_selection = cmds.ls(sl=True, type="transform") or [] pin_outputs = [] for obj in rivet_selection: if "pinOutput" in obj: pin_outputs.append(obj) if not pin_outputs: cmds.warning( "没有找到 Maya 创建的 pinOutput。" ) return # ----------------------------------------------------- # 如果选择多个组件,会创建多个 pinOutput # ----------------------------------------------------- created_locators = [] for pin_output in pin_outputs: # 创建新的真正 Locator locator = cmds.spaceLocator( name="bakedLocator#" )[0] # ------------------------------------------------- # 先让 Locator 对齐 pinOutput # ------------------------------------------------- cmds.delete( cmds.parentConstraint( pin_output, locator, mo=False ) ) # ------------------------------------------------- # 再建立 Parent Constraint # ------------------------------------------------- constraint = cmds.parentConstraint( pin_output, locator, mo=False )[0] created_locators.append( (locator, constraint, pin_output) ) # ----------------------------------------------------- # 重新选择 Locator # ----------------------------------------------------- cmds.select( [x[0] for x in created_locators], r=True ) # ----------------------------------------------------- # UI 显示 # ----------------------------------------------------- cmds.inViewMessage( amg="<hl>Rivet Locator Created</hl>", pos="midCenter", fade=True ) print("====================================") print("Rivet Locator 创建完成") print("uvPin :", uvpin) print("Locator :", [x[0] for x in created_locators]) print("====================================") # --------------------------------------------------------- # Bake Locator # --------------------------------------------------------- def bake_selected_locators(*args): """ Bake 当前选择的 Locator。 Bake 后: pinOutput ↓ Constraint ↓ Locator 变成: Locator translate / rotate 每帧都有 key """ locators = cmds.ls(sl=True, type="transform") if not locators: cmds.warning( "请先选择需要 Bake 的 Locator。" ) return start = cmds.playbackOptions( q=True, min=True ) end = cmds.playbackOptions( q=True, max=True ) # ----------------------------------------------------- # Bake # ----------------------------------------------------- cmds.bakeResults( locators, t=(start, end), simulation=True, sampleBy=1, disableImplicitControl=True, preserveOutsideKeys=True, sparseAnimCurveBake=False, removeBakedAttributeFromLayer=False, bakeOnOverrideLayer=False, minimizeRotation=True ) # ----------------------------------------------------- # 删除 Locator 上的约束 # ----------------------------------------------------- for locator in locators: constraints = cmds.listRelatives( locator, type="constraint" ) or [] if constraints: cmds.delete(constraints) cmds.select(locators, r=True) cmds.inViewMessage( amg="<hl>Locator Bake Complete</hl>", pos="midCenter", fade=True ) print("Bake 完成:") print(locators) # --------------------------------------------------------- # Bake + 清理 # --------------------------------------------------------- def bake_and_cleanup(*args): """ 一键完成: Bake ↓ 删除 Parent Constraint ↓ 删除 pinOutput ↓ 删除 uvPin """ locators = cmds.ls(sl=True, type="transform") if not locators: cmds.warning( "请先选择需要 Bake 的 Locator。" ) return start = cmds.playbackOptions( q=True, min=True ) end = cmds.playbackOptions( q=True, max=True ) # ----------------------------------------------------- # Bake # ----------------------------------------------------- cmds.bakeResults( locators, t=(start, end), simulation=True, sampleBy=1, disableImplicitControl=True, preserveOutsideKeys=True, sparseAnimCurveBake=False, minimizeRotation=True ) # ----------------------------------------------------- # 删除约束 # ----------------------------------------------------- for locator in locators: constraints = cmds.listRelatives( locator, type="constraint" ) or [] if constraints: cmds.delete(constraints) # ----------------------------------------------------- # 删除对应 pinOutput # ----------------------------------------------------- for locator in locators: # 根据 Locator 名字无法可靠找到 pinOutput, # 所以这里通过场景中的连接关系查找。 connections = cmds.listConnections( locator, s=True, d=False ) or [] for node in connections: if cmds.nodeType(node) == "uvPin": outputs = cmds.listConnections( node + ".outputMatrix", s=False, d=True ) or [] for output in outputs: if cmds.objExists(output): try: cmds.delete(output) except: pass try: cmds.delete(node) except: pass cmds.select(locators, r=True) cmds.inViewMessage( amg="<hl>Bake + Cleanup Complete</hl>", pos="midCenter", fade=True ) # --------------------------------------------------------- # Alembic Export # --------------------------------------------------------- def export_selected_abc(*args): """ 导出当前选择的 Locator 为 Alembic。 """ locators = cmds.ls(sl=True, type="transform") if not locators: cmds.warning( "请先选择需要导出的 Locator。" ) return # ----------------------------------------------------- # 获取路径 # ----------------------------------------------------- result = cmds.fileDialog2( fileMode=0, caption="Export Alembic", fileFilter="Alembic (*.abc)" ) if not result: return filepath = result[0] if not filepath.lower().endswith(".abc"): filepath += ".abc" # ----------------------------------------------------- # 时间范围 # ----------------------------------------------------- start = cmds.playbackOptions( q=True, min=True ) end = cmds.playbackOptions( q=True, max=True ) # ----------------------------------------------------- # 加载 AbcExport # ----------------------------------------------------- if not cmds.pluginInfo( "AbcExport", q=True, loaded=True ): try: cmds.loadPlugin("AbcExport") except: cmds.warning( "无法加载 AbcExport 插件。" ) return # ----------------------------------------------------- # Alembic Job # ----------------------------------------------------- root_flags = "" for locator in locators: root_flags += ' -root "{}"'.format(locator) job = ( "-frameRange {} {} " "-uvWrite " "-worldSpace " "-writeVisibility " "-dataFormat ogawa " "{} " '-file "{}"' ).format( start, end, root_flags, filepath ) try: cmds.AbcExport( j=job ) except Exception as e: cmds.warning( "Alembic 导出失败:{}".format(e) ) return cmds.inViewMessage( amg="<hl>ABC Export Complete</hl>", pos="midCenter", fade=True ) print("Alembic exported:") print(filepath) # --------------------------------------------------------- # 一键流程 # --------------------------------------------------------- def create_bake_export(*args): """ 一键: Component ↓ Rivet ↓ Locator ↓ Bake ↓ Cleanup ↓ Export ABC """ create_rivet_locator() cmds.refresh() locators = cmds.ls( sl=True, type="transform" ) if not locators: return bake_and_cleanup() cmds.refresh() export_selected_abc() # --------------------------------------------------------- # UI # --------------------------------------------------------- def show_ui(): if cmds.window( WINDOW, exists=True ): cmds.deleteUI(WINDOW) window = cmds.window( WINDOW, title="Rivet Locator Tool", sizeable=True, widthHeight=(420, 420) ) cmds.columnLayout( adjustableColumn=True, rowSpacing=8 ) cmds.separator( height=10, style="none" ) cmds.text( label="Rivet Locator Tool", font="boldLabelFont", height=30 ) cmds.text( label="Maya 2022 / UV Pin Rivet Workflow", height=20 ) cmds.separator( height=10, style="in" ) # ----------------------------------------------------- # Step 1 # ----------------------------------------------------- cmds.text( label="STEP 1 | 选择模型组件", align="left", font="boldLabelFont" ) cmds.text( label="Vertex / Edge / Face 均可", align="left" ) cmds.separator( height=8, style="none" ) cmds.button( label="① 创建 Rivet Locator", height=45, backgroundColor=(0.25, 0.45, 0.65), command=create_rivet_locator ) # ----------------------------------------------------- # Step 2 # ----------------------------------------------------- cmds.text( label="STEP 2 | Bake", align="left", font="boldLabelFont" ) cmds.button( label="② Bake Locator", height=40, command=bake_selected_locators ) cmds.button( label="③ Bake + Cleanup Rivet", height=40, command=bake_and_cleanup ) # ----------------------------------------------------- # Step 3 # ----------------------------------------------------- cmds.text( label="STEP 3 | Alembic", align="left", font="boldLabelFont" ) cmds.button( label="④ Export Selected Locator → ABC", height=45, backgroundColor=(0.35, 0.55, 0.35), command=export_selected_abc ) cmds.separator( height=10, style="in" ) # ----------------------------------------------------- # One Click # ----------------------------------------------------- cmds.button( label="一键:Rivet → Bake → Cleanup → ABC", height=50, backgroundColor=(0.55, 0.40, 0.20), command=create_bake_export ) cmds.separator( height=10, style="none" ) cmds.showWindow(window) # --------------------------------------------------------- # Run # --------------------------------------------------------- show_ui()

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询