口形同步

最終更新: 2022年10月6日

识别口形同步参数

您可以使用口形同步效果将口形同步动作应用于模型。
要应用口形同步效果,请执行以下处理:

  • 与应用.model3.json文件中描述的口形同步效果值的参数相关联
  • 通过语音输入、动态或其他方法将数值传递给口形同步效果

其中,.model3.json文件中描述的口形同步效果和参数的关联信息可以通过实装ICubismModelSetting接口的CubismModelSettingJson类获取。

// C++
for (csmInt32 i = 0; i < _modelSetting->GetLipSyncParameterCount(); ++i)
{
   CubismIdHandle lipsyncParameter = _modelSetting->GetLipSyncParameterId(i);
}
// TypeScript
for(let i: number = 0; i < _modelSetting.getLipSyncParameterCount(); ++i)
{
    let lipSyncParameter = _modelSetting.getLipSyncParameterId(i);
}
// Java
for(int i = 0; i < _modelSetting.getLipSyncParameterCount(); i++) {
CubismId lipSyncParameter = _modelSetting.getLipSyncParameterId(i);
}

请确认“眨眼设置”以将定义放入.model3.json文件中。
如果在Editor上设置了自动眨眼和口形同步并输出,则.model3.json文件将描述如下。

{
    ... 省略 ...
       "Groups": [
                {
                        "Target": "Parameter",
                        "Name": "LipSync",
                        "Ids": [
                                "ParamMouthOpenY"
                        ]
                },
                {
                        "Target": "Parameter",
                        "Name": "EyeBlink",
                        "Ids": [
                                "ParamEyeLOpen",
                                "ParamEyeROpen"
                        ]
                }
        ]
}

进行口形同步的3种方法

口形同步大致分为以下三种类型。

1. 实时获取音量,并直接指定开合度的方法

通过某种方式获取音频电平,并根据对象参数指定音阶,
实现实时口形同步。

// C++
csmFloat32 value = GetAudioLevelExampleFunction(); // 获取最新的音频电平。

for (csmInt32 i = 0; i < _modelSetting->GetLipSyncParameterCount(); ++i)
{
    _model->AddParameterValue(_modelSetting->GetLipSyncParameterId(i), value, 0.8f);
}
// TypeScript
let value: number = getAudioLevelExampleFunction(); // 获取最新的音频电平。

for(let i: number = 0; i < _modelSetting.getLipSyncParameterCount(); ++i)
{
    _model.addParameterValue(_modelSetting.getLipSyncParameterId(i), value, 0.8);
}
// Java
float value = getAudioLevelExampleFunction(); // 获取最新的音频电平。
for(int i = 0; i < _modelSetting.getLipSyncParameterCount(); i++){
_model.addParameterValue((_modelSetting.getLipSyncParameterId(i), value, 0.8f);
}

在Native(C++)的CubismModel::Update函数、Web(TypeScript)、Java的CubismModel.update函数前,通过对Native(C++)的CubismModel::SetParameterValue函数、Web(TypeScript)、Java的CubismModel.setParameterValue函数以及Native(C++)的CubismModel::AddParameterValue函数、Web(TypeScript)、Java的CubismModel.addParameterValue函数的第二个参数直接设置0~1的值,可以控制嘴部打开的程度。

对于iPhone/Android 2.3或更高版本(*),您可以实时获取播放过程中的音量。
您可以通过将获取的播放期间音量值处理到0 .. 1的范围,并使用上述命令设置该值来进行口形同步。
(因为嘴巴的开合是按照标准参数设置用0到1的参数创建的)

即使要设置的值小于0或大于1,也不会发生错误,但在这种情况下,口形同步可能无法正常动作。
(*):对于Android 2.2及更早版本,无法在执行时获取正在播放的音量。
其他平台能否实时获取音量取决于音频播放库。

如何使用iPhone获取:AVAudioPlayer类
如何使用Android获取:Visualizer类

2. 使用带有口形同步用信息的动态的方法

这是一种通过在Editor上作业,将声音的动作融入动态本身的方法。
有关如何将口形同步动作添加到动态,请参考“创建使用BGM和音频的场景”
如果您使用Native(C++)的CubismMotion::SetEffectIds函数或Web(TypeScript)、Java的CubismMotion.setEffectIds函数在播放前设置口形同步和眨眼参数群,则在CubismMotion副本参数更新处理中,将其替换为对象参数后播放动态。

// C++
    // 导入.model3.json中描述的眨眼参数
    csmVector<CubismIdHandle> eyeBlinkIds;
    csmInt32 eyeBlinkCount = _modelSetting->GetEyeBlinkParameterCount();
    for (csmInt32 i = 0; i < eyeBlinkCount; i++)
    {
        eyeBlinkIds.PushBack(_modelSetting->GetEyeBlinkParameterId(i));
    }

    // 导入.model3.json中描述的口形同步参数
    csmVector<CubismIdHandle> lipSyncIds;
    csmInt32 lipSyncCount = _modelSetting->GetLipSyncParameterCount();
    for (csmInt32 i = 0; i < lipSyncCount; i++)
    {
        lipSyncIds.PushBack(_modelSetting->GetLipSyncParameterId(i));
    }

    // 导入动态文件
    csmByte* buffer;
    csmSizeInt size;
    csmString path = "example.motion3.json";
    buffer = CreateBuffer(path.GetRawString(), &size);
    CubismMotion* tmpMotion = static_cast<CubismMotion*>(LoadMotion(buffer, size, name.GetRawString()));
    DeleteBuffer(buffer, path.GetRawString());

    // 在导入的动态文件中注册眨眼参数和口形同步参数
    tmpMotion->SetEffectIds(eyeBlinkIds, lipSyncIds);
// TypeScript
    // 导入.model3.json中描述的眨眼参数
    let eyeBlinkIds: csmVector<CubismIdHandle> = new csmVector<CubismIdHandle>();
    let eyeBlinkCount: number = _modelSetting.getEyeBlinkParameterCount();
    for(let i: number = 0; i < eyeBlinkCount; i++)
    {
        eyeBlinkIds.pushBack(_modelSetting.getEyeBlinkParameterId(i));
    }

    // 导入.model3.json中描述的口形同步参数
    let lipSyncIds: csmVector<CubismIdHandle> = new csmVector<CubismIdHandle>();
    let lipSyncCount: number = _modelSetting.getLipSyncParameterCount();
    for(let i: number = 0; i < lipSyncCount; i++)
    {
        lipSyncIds.pushBack(_modelSetting.getLipSyncParamterId(i));
    }

    // 导入动态文件
    let path: string = fileName;
    path = this._modelHomeDir + path;

    fetch(path).then(
        (response) =>
        {
            return response.arrayBuffer();
        }
    ).then(
        (arrayBuffer) =>
        {
            let buffer: ArrayBuffer = arrayBuffer;
            let size = buffer.byteLength;
            let tmpMotion = <CubismMotion>this.loadMotion(buffer, size, name);
            deleteBuffer(buffer, path);

            // 作为导入的一环,在动态文件中注册眨眼参数和口形同步参数
            motion.setEffectIds(this._eyeBlinkIds, this._lipSyncIds);
        }
    );
// Java
    // 导入.model3.json中描述的眨眼参数
    List<CubismId> eyeBlinkIds;
    int eyeBlinkCount = _modelSetting.getEyeBlinkParameterCount();
    for (int i = 0; i < eyeBlinkCount; i++) {
        eyeBlinkIds.add(_modelSetting.getEyeBlinkParameterId(i));
    }
    // 导入.model3.json中描述的口形同步参数
    List<CubismId> lipSyncIds;
    int lipSyncCount = _modelSetting.getLipSyncParameterCount();
    for (int i = 0; i < lipSyncCount; i++) {
        lipSyncIds.add(_modelSetting.getLipSyncParameterId(i));
    }
    // 导入动态文件
    byte[] buffer;
    String path = "example.motion3.json";
    buffer = createBuffer(path);
    CubismMotion tmpMotion = loadMotion(buffer);
    // 在导入的动态文件中注册眨眼参数和口形同步参数
    tmpMotion.setEffectIds(eyeBlinkIds, lipSyncIds);

3. 使用仅口形同步用信息的动态的方法

Native:

这是准备专门处理2中处理的动态的动态管理器,只控制嘴巴的方法。
当您想将身体和头部动作动态与口形同步分开时,十分有用。

// C++
    /**
     * @brief 用户实际使用的模型的实装类<br>
     * 进行模型生成、功能组件生成、更新处理和渲染调用。
     *
     */
    class LAppModel : public Csm::CubismUserModel
    {
    /*省略*/  

    private:    
        CubismMotionManager*    _mouthMotionManager; // <<< 追加
    };
// C++
    void LAppModel::Update()
    {
 /*省略*/  

        //-----------------------------------------------------------------
        _model->LoadParameters(); // 读取上次保存的状态
        if (_motionManager->IsFinished())
        {
            // 如果没有动态播放,则从待机动态开始随机播放
            StartRandomMotion(MotionGroupIdle, PriorityIdle);
        }
        else
        {
            const csmFloat32 playSpeed = pow(2, (csmFloat32)_motionSpeed / 10.0);
            motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds * playSpeed); // 更新动态
        }
        _mouthMotionManager->UpdateMotion(_model, deltaTimeSeconds); // <<< 追加
        _model->SaveParameters(); // 保存状态
        //-----------------------------------------------------------------

/*省略*/  

    }
// C++
	_mouseMotionManager->StartMotionPriority(lipsyncMotion, autoDelete, priority);

Web:

这是准备专门处理2中处理的动态的动态管理器,只控制嘴巴的方法。
当您想将身体和头部动作动态与口形同步分开时,十分有用。

// TypeScript
    export class LAppModel extends CubismUserModel {
/*省略*/

        _mouthMotionManager: CubismMotionManager; // <<< 追加
    }
// TypeScript
	public update(): void
    {
/*省略*/

        //--------------------------------------------------------------------------
        this._model.loadParameters();   // 读取上次保存的状态
        if(this._motionManager.isFinished())
        {
            // 如果没有动态播放,则从待机动态开始随机播放
            this.startRandomMotion(LAppDefine.MotionGroupIdle, LAppDefine.PriorityIdle);
            
        }
        else
        {
            motionUpdated = this._motionManager.updateMotion(this._model, deltaTimeSeconds);    // 更新动态
        }
		_mouthMotionManager.udpateMotion(_model, deltaTimeSeconds); // <<< 追加
        this._model.saveParameters(); // 保存状态
        //--------------------------------------------------------------------------

/*省略*/
    }
// TypeScript
	_mouseMotionManager.startMotionPriority(lipSyncMotion, autoDelete, priority);

Java: 

这是准备专门处理2中处理的动态的动态管理器,只控制嘴巴的方法。
当您想将身体和头部动作动态与口形同步分开时,十分有用。

// Java
/**
 * 用户实际使用的模型的实装类
 * 进行模型生成、功能组件生成、更新处理和渲染调用。
 */
public class LAppModel extends CubismUserModel {
    // 省略

    private CubismMotionManager _mouthMotionManager; // <<< 追加
}
// Java
public void update() {
/*省略*/
    // -----------------------------
    _model.loadParameters(); // 读取上次保存的状态
    if (_motionManager.isFinished()) {
        // 如果没有动态播放,则从待机动态开始随机播放
        startRandomMotion(LAppDefine.MotioNGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority());
    } else {
        final float playSpeed = Math.pow(2, _motionSpeed / 10);
        isMotionUpdated = _motionManager.updateMotion(_model, deltaTimeSeconds * playSpeed); // 更新动态
    }  
    _mouthMotionManager.updateMotion(_model, deltaTimeSeconds); // <<< 追加
    _model.saveParameters(); // 保存状态
    // -----------------------------
/*省略*/
}
// Java
_mouseMotionManager.startMotionPriority(lipSyncMotion, autoDelete, priority);
关于本报道,敬请提出您的意见及要求。