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