【maya】在maya中对物体创建locator定位器做跟踪
用途灯光跟随 or 对角色某个顶点跟踪1、创建locator又名定位器有多种创建方式可以在创建下拉菜单中创建也可直接在绑定面板创建方法1方法22、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(slTrue, flTrue) 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(slTrue, typetransform) 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(typeuvPin) 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, rTrue) cmds.Rivet() except Exception as e: cmds.warning( Rivet 创建失败{}.format(e) ) return # ----------------------------------------------------- # 找新创建的 uvPin # ----------------------------------------------------- uvpin get_new_uvpin(before_uvpin) # ----------------------------------------------------- # 找 pinOutput # ----------------------------------------------------- rivet_selection cmds.ls(slTrue, typetransform) 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( namebakedLocator# )[0] # ------------------------------------------------- # 先让 Locator 对齐 pinOutput # ------------------------------------------------- cmds.delete( cmds.parentConstraint( pin_output, locator, moFalse ) ) # ------------------------------------------------- # 再建立 Parent Constraint # ------------------------------------------------- constraint cmds.parentConstraint( pin_output, locator, moFalse )[0] created_locators.append( (locator, constraint, pin_output) ) # ----------------------------------------------------- # 重新选择 Locator # ----------------------------------------------------- cmds.select( [x[0] for x in created_locators], rTrue ) # ----------------------------------------------------- # UI 显示 # ----------------------------------------------------- cmds.inViewMessage( amghlRivet Locator Created/hl, posmidCenter, fadeTrue ) 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(slTrue, typetransform) if not locators: cmds.warning( 请先选择需要 Bake 的 Locator。 ) return start cmds.playbackOptions( qTrue, minTrue ) end cmds.playbackOptions( qTrue, maxTrue ) # ----------------------------------------------------- # Bake # ----------------------------------------------------- cmds.bakeResults( locators, t(start, end), simulationTrue, sampleBy1, disableImplicitControlTrue, preserveOutsideKeysTrue, sparseAnimCurveBakeFalse, removeBakedAttributeFromLayerFalse, bakeOnOverrideLayerFalse, minimizeRotationTrue ) # ----------------------------------------------------- # 删除 Locator 上的约束 # ----------------------------------------------------- for locator in locators: constraints cmds.listRelatives( locator, typeconstraint ) or [] if constraints: cmds.delete(constraints) cmds.select(locators, rTrue) cmds.inViewMessage( amghlLocator Bake Complete/hl, posmidCenter, fadeTrue ) print(Bake 完成) print(locators) # --------------------------------------------------------- # Bake 清理 # --------------------------------------------------------- def bake_and_cleanup(*args): 一键完成 Bake ↓ 删除 Parent Constraint ↓ 删除 pinOutput ↓ 删除 uvPin locators cmds.ls(slTrue, typetransform) if not locators: cmds.warning( 请先选择需要 Bake 的 Locator。 ) return start cmds.playbackOptions( qTrue, minTrue ) end cmds.playbackOptions( qTrue, maxTrue ) # ----------------------------------------------------- # Bake # ----------------------------------------------------- cmds.bakeResults( locators, t(start, end), simulationTrue, sampleBy1, disableImplicitControlTrue, preserveOutsideKeysTrue, sparseAnimCurveBakeFalse, minimizeRotationTrue ) # ----------------------------------------------------- # 删除约束 # ----------------------------------------------------- for locator in locators: constraints cmds.listRelatives( locator, typeconstraint ) or [] if constraints: cmds.delete(constraints) # ----------------------------------------------------- # 删除对应 pinOutput # ----------------------------------------------------- for locator in locators: # 根据 Locator 名字无法可靠找到 pinOutput # 所以这里通过场景中的连接关系查找。 connections cmds.listConnections( locator, sTrue, dFalse ) or [] for node in connections: if cmds.nodeType(node) uvPin: outputs cmds.listConnections( node .outputMatrix, sFalse, dTrue ) or [] for output in outputs: if cmds.objExists(output): try: cmds.delete(output) except: pass try: cmds.delete(node) except: pass cmds.select(locators, rTrue) cmds.inViewMessage( amghlBake Cleanup Complete/hl, posmidCenter, fadeTrue ) # --------------------------------------------------------- # Alembic Export # --------------------------------------------------------- def export_selected_abc(*args): 导出当前选择的 Locator 为 Alembic。 locators cmds.ls(slTrue, typetransform) if not locators: cmds.warning( 请先选择需要导出的 Locator。 ) return # ----------------------------------------------------- # 获取路径 # ----------------------------------------------------- result cmds.fileDialog2( fileMode0, captionExport Alembic, fileFilterAlembic (*.abc) ) if not result: return filepath result[0] if not filepath.lower().endswith(.abc): filepath .abc # ----------------------------------------------------- # 时间范围 # ----------------------------------------------------- start cmds.playbackOptions( qTrue, minTrue ) end cmds.playbackOptions( qTrue, maxTrue ) # ----------------------------------------------------- # 加载 AbcExport # ----------------------------------------------------- if not cmds.pluginInfo( AbcExport, qTrue, loadedTrue ): 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( jjob ) except Exception as e: cmds.warning( Alembic 导出失败{}.format(e) ) return cmds.inViewMessage( amghlABC Export Complete/hl, posmidCenter, fadeTrue ) 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( slTrue, typetransform ) if not locators: return bake_and_cleanup() cmds.refresh() export_selected_abc() # --------------------------------------------------------- # UI # --------------------------------------------------------- def show_ui(): if cmds.window( WINDOW, existsTrue ): cmds.deleteUI(WINDOW) window cmds.window( WINDOW, titleRivet Locator Tool, sizeableTrue, widthHeight(420, 420) ) cmds.columnLayout( adjustableColumnTrue, rowSpacing8 ) cmds.separator( height10, stylenone ) cmds.text( labelRivet Locator Tool, fontboldLabelFont, height30 ) cmds.text( labelMaya 2022 / UV Pin Rivet Workflow, height20 ) cmds.separator( height10, stylein ) # ----------------------------------------------------- # Step 1 # ----------------------------------------------------- cmds.text( labelSTEP 1 | 选择模型组件, alignleft, fontboldLabelFont ) cmds.text( labelVertex / Edge / Face 均可, alignleft ) cmds.separator( height8, stylenone ) cmds.button( label① 创建 Rivet Locator, height45, backgroundColor(0.25, 0.45, 0.65), commandcreate_rivet_locator ) # ----------------------------------------------------- # Step 2 # ----------------------------------------------------- cmds.text( labelSTEP 2 | Bake, alignleft, fontboldLabelFont ) cmds.button( label② Bake Locator, height40, commandbake_selected_locators ) cmds.button( label③ Bake Cleanup Rivet, height40, commandbake_and_cleanup ) # ----------------------------------------------------- # Step 3 # ----------------------------------------------------- cmds.text( labelSTEP 3 | Alembic, alignleft, fontboldLabelFont ) cmds.button( label④ Export Selected Locator → ABC, height45, backgroundColor(0.35, 0.55, 0.35), commandexport_selected_abc ) cmds.separator( height10, stylein ) # ----------------------------------------------------- # One Click # ----------------------------------------------------- cmds.button( label一键Rivet → Bake → Cleanup → ABC, height50, backgroundColor(0.55, 0.40, 0.20), commandcreate_bake_export ) cmds.separator( height10, stylenone ) cmds.showWindow(window) # --------------------------------------------------------- # Run # --------------------------------------------------------- show_ui()