• 关于使用 Sequence Recorder 无法录制面部 Morph Target 问题的解决方法


    关于使用 Sequence Record 无法录制面部 Morph Target 问题的解决方法

    这里 Sequence Recorder 包含动画蓝图中默认的录制窗口:

    20220609165131

    总体上来说有两种方法,一种是基于蓝图和代理骨骼,一种是通过C++代码实现。

    1. 蓝图加代理骨骼的实现

    主要是通过这个节点实现:

    20220607165201

    选中节点,然后选中充当代理的骨骼(注意,最好是动画用不到的骨骼,不然大概率会出现奇怪的效果,如果没有这种可以自己在导入引擎后的 Skeleton 文件中创建虚拟骨骼):

    20220607165401

    上图中选中了 TONGLETranslation X 作为数据来源,即骨骼的坐标 X 值作为数据来源。

    再设置下要设置数据的目标:

    20220607165756

    这里设置的目标类型是名为 jawOpen 的 Morph Target。此外,目标也可以设置为另一个骨骼,以及 Material Parameter,这里不作展开。

    最后类似这样连接,就可以通过 TONGLE 骨骼的坐标 X 来控制 jawOpen 了:

    20220607170348

    最左侧的 Transform (Modyfy) Bone 只是演示用,实际上工程中都是动画源。

    1.1. 优缺点

    优点:

    1. 蓝图实现方便,能够快速验证;
    2. 适用于只需要更新少量 Morph Target 的场合;

    缺点:

    1. Morph Target 一多起来(例如 ARKit 那种52个 Morph Target 的情况)就会很难管理,代码会很杂乱(这算是蓝图通病);
    2. 不适合协作修改(蓝图 Merge 起来还是不太方便);
    3. 可能需要创建大量的代理骨骼(一个骨骼最多只能传数据到9个 Morph Target);

    2. 代码实现

    我比较推荐通过代码实现,总体步骤是创建插件,创建新的继承AnimNode_Base动画节点(或者其他继承AnimNode_Base的子类例如AnimNode_SkeletalControlBase),然后在动画节点的Evaluate_AnyThread(如果是其他AnimNode_Base的子类那么可能是EvaluateXXX_AnyThread,例如AnimNode_SkeletalControlBaseEvaluateComponentSpace_AnyThread)。下面是详细步骤。

    2.1. 创建插件

    这一步没什么好说的,直接通过引擎 Plugin 界面创建就行:

    20220607163818

    插件名字等内容可以随意填,插件类型建议选Blank:

    20220609164936

    2.2. 创建动画节点

    这里以AnimNode_Base作为基类为例子,实际上可以用别的动画节点类,可以根据业务需求选择。

    我们重点看AnimNode部分,除了AnimNode还需要创建AnimGraphNode,这里不详细说,因为创建默认的就可以了。

    假设我们创建的类为:

    USTRUCT(BlueprintInternalUseOnly)
    struct DATALINK_API FAnimNode_DLMoCap : public FAnimNode_Base {
        // 这个一定要有
        GENERATED_USTRUCT_BODY()
        // ...这里先省略
    }
    

    重点是重载FAnimNode_Base的这个函数:

    virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
    virtual void Update_AnyThread(const FAnimationUpdateContext & Context) override;
    virtual void Evaluate_AnyThread(FPoseContext& Output) override;
    

    其他函数基本上不怎么需要修改,而Initialize_AnyThreadUpdate_AnyThread主要用来初始化、加载数据,所以实际上重点只是Evaluate_AnyThread。在Evaluate_AnyThread函数中,最重要的部分则是:

    void FAnimNode_DLMoCap::Evaluate_AnyThread(FPoseContext& Output)
    {
        // 忽略这一行,主要是用来接受上游动画节点的动画数据,和我们要做的事情不太相关
        SourcePose.Evaluate(Output);
    
        // 获取骨骼
        const USkeleton* Skeleton = Output.AnimInstanceProxy->GetSkeleton();
        // 从骨骼上获取UID,实际上相当于是根据名字获取动画曲线 Curve 的 UID
        // 这里的 jawOpen 最好是一个 FName 类型的变量,这样方便传入 Morph Target 名字,这里只是演示用所以直接输入了名字
        const SmartName::UID_Type NameUID = Skeleton->GetUIDByName(USkeleton::AnimCurveMappingName, "jawOpen");
        if (NameUID != SmartName::MaxUID)
        {
            // 这里的 1.0f 最好是一个 float 变量,这一行相当于设置曲线当前值为 1.0f
            Output.Curve.Set(NameUID, 1.0f);
        }
    }
    

    我对这里操作的理解是,创建一个对应给定 Morph Target 的 Curve(获取 UID 的时候是通过名字获取的,所以如果输入的 Morph Target 名字是模型没有的 Morph Target,那么应该就会获取失败,最终返回结果会是 SmartName::MaxUID),然后写入当前帧对应的值。

    最后录制好之后我们可以直接打开动画文件,看到 Morph Target 的曲线:

    20220609164527

    此时表情数据也可以正常导出为 FBX 文件给美术进一步修改、使用。

  • 相关阅读:
    css数学运算函数 calc(),和css的数学运算
    MySQL设置字段的默认值为当前系统时间
    今天阿里云服务器被挂马wnTKYg挖矿的清理
    linux shell常用命令
    无损扩容,调整Centos6.5服务器分区大小,不适用centos7,centos6.5 调整逻辑卷大小
    添加远程库
    interface 设置默认值
    radio根据value值动态选中
    获取下拉js 具体值
    mysql中int、bigint、smallint 和 tinyint的存储
  • 原文地址:https://www.cnblogs.com/yejianying/p/record_morph_target_with_ue4_sequence_recorder.html
Copyright © 2020-2023  润新知