口形同步
最終更新: 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);