【maya】在maya中对物体创建locator定位器做跟踪

用途:灯光跟随 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脚本

python 复制代码
# -*- 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()
相关推荐
3D小将1 个月前
3D格式转换之PROE 转 CATIA 标准化转换技术
3d·maya·solidworks模型·sketchup模型·igs模型
成都渲染101云渲染66662 个月前
GPT Image 2 + Seedance 2.0制作AI动漫短片教程
ai·blender·maya
成都渲染101云渲染66662 个月前
CR15新功能介绍以及CR15云渲染流程
ue5·图形渲染·blender·maya·corona
3D小将3 个月前
Blender 模型转 glTF 2.0 完整技术手册(本地导出 +免费在线转换)
maya·solidworks模型·ug模型·stl模型
3D小将3 个月前
3D格式转换之3DXML 转换为 GLTF 技术文档
3d·maya·solidworks模型·ug模型·catia模型·stl模型
成都渲染101云渲染66664 个月前
极速渲染的办法,大量算力铺垫下的“云渲染”究竟有多快
ue5·maya
小年糕是糕手4 个月前
【Blender】建模篇终章最后一舞!手把手拿捏 Blender 卡通小狐狸建模(带你完整理清建模思路)
3dsmax·blender·maya
小年糕是糕手4 个月前
【Blender】Blender 曲线建模全解析:贝塞尔 / NURBS / 蒙皮 / 置换,从原理到实战
3dsmax·blender·maya
小年糕是糕手4 个月前
【Blender】Blender 树叶建模全攻略:3 种叶片 + 阵列 / 镜像 / 晶格工具实战精讲
3dsmax·blender·maya