LipSync口型离线解析(下)
上篇文章中我们介绍了如何激活Lipsync工具以及使用Lip-sync进行wav音频文件的离线分析,同样我们介绍了简单的如何通过bat文件进行批处理,来完成大量wav文件的解析工作。
当我们生成了相关声音数据后,我们需要在引擎或3D Maya中进行测试,以此来验证和完成我们的口型同步过程。
本文主要使用3D Maya工具进行数据的验证,以及Unity和Unreal中导入相关数据后进行口型同步内容制作。
3D Maya中数据验证
通过3D Maya验证数据是通过将模型导入3D Maya,添加相关脚本文件,wav音频文件以及音频口型数据文件后在3D Maya中进行播放时,能够同步变更模型变形来测试口型变化效果。
主要步骤如下:
- 首先打开3D Maya将模型直接导入到3D Maya的场景中:
- 将音频文件拖拽到Maya的时间轴上
- 读取/执行脚本文件
执行ADXLipSync2Maya脚本。腳本位于以下路径。cri\tools\ADXLipSync\samples\ADXLipSync2Maya.mel从资源管理器将ADXLipSync2Maya.mel拖放到Maya的视图(Viewport)中。(读取并立即执行) -
.执行完成后,会弹出界面,点击界面中的Browse…按钮,选择我们已经通过Lipsync分析完成的adxlip文件:完成后点击Apply
-
完成后等待处理完成点击播放即可看到效果,
完成后即可看到最终效果。
Unity口型数据同步处理
我们通过使用Lipsync工具,分析wav音频文件后得到了adxlip格式的文本数据,为了能够使用这些数据,我们可以将adxlip的后缀更改为csv,作为csv文件导入到Unity中进行使用。
CSV文件导入
我们通过将adxlip后缀的文件更改后缀名为csv后,可以直接导入到Unity中。将相关的csv文件直接拖入到Resources文件夹中:
Unity中相关处理:
1.创建一个新的场景,并将模型拖入到场景中
2.新增一个名称为csvController的类,相关代码如下:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
public class csvController
{
static csvController csv;
public List<string[]> arrayData;
private csvController() //单例,构造方法为私有
{
arrayData = new List<string[]>();
}
public static csvController GetInstance() //单例方法获取对象
{
if (csv == null)
{
csv = new csvController();
}
return csv;
}
public void loadFile(string path, string fileName)
{
arrayData.Clear();
StreamReader sr = null;
try
{
string file_url = path + "//" + fileName; //根据路径打开文件
sr = File.OpenText(file_url);
Debug.Log("File Find in " + file_url);
}
catch
{
Debug.Log("File cannot find ! ");
return;
}
string line;
while ((line = sr.ReadLine()) != null) //按行读取
{
arrayData.Add(line.Split(',')); //每行逗号分隔,split()方法返回 string[]
}
sr.Close();
sr.Dispose();
}
public string getString(int row, int col)
{
return arrayData[row][col];
}
public int getInt(int row, int col)
{
return int.Parse(arrayData[row][col]);
}
}
此类的作用主要用于读取csv文件,将csv数据存储于一个list泛型函数中。
3.完成脚本后,我们在场景中创建一个空的object,并在其上添加一个新建名称为ModleControl的脚本,脚本内容如下:
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
public class ModleControl : MonoBehaviour
{
public SkinnedMeshRenderer mesh; // 口型模型匹配
public AudioSource audio; // 播放的音频内容
private int date = 3; // 每帧刷新时增加,用于当帧刷新时更改csv列数据
private float width; // 口型宽度数据
private float height; // 口型高度数据
private bool lipsyncon = false; // 数据刷新判断参数
private bool japanesemodel = false; // 口型分析数据切换模式
private bool model = false; // 口型分析数据切换判断
int i = 0;
void Start()
{
//csvController加载csv文件,单例模式,这个类只有一个对象,这个对象只能加载一个csv文件
csvController.GetInstance().loadFile(Application.dataPath + "/Resources", "WellcometoCRIwareChina.csv");
}
void Update()
{
if (i % 2 == 0) // 刷新率控住为2帧刷新一次数据
{
if (lipsyncon == true) // 是否进行口型数据刷新
{
if (int.Parse(csvController.GetInstance().getString(date + 1, 0)) >= 194) // csv数据保护,防止无限增长超过范围,同时当不在变化口型时将所有口型变形数据还原
{
for (i = 0; i <= 8 ; i++) // 还原口型变形数据的所有信息
{
mesh.SetBlendShapeWeight(i, 0);
}
date = 3;
lipsyncon = false;
}
else
{
if (japanesemodel == false) // 高度宽度模式更新变形
{
width = float.Parse(csvController.GetInstance().getString(date, 2)) * 100;
height = float.Parse(csvController.GetInstance().getString(date, 3)) * 100;
date++;
mesh.SetBlendShapeWeight(0, height);
if (width >= 58.3)
{
mesh.SetBlendShapeWeight(1, (width - 58.3f) * 2.4f);
}
else
{
mesh.SetBlendShapeWeight(2, -1.72f * width + 100);
}
}
else // 日文元音模式更新变形
{
mesh.SetBlendShapeWeight(4, float.Parse(csvController.GetInstance().getString(date, 5)) * 100);
mesh.SetBlendShapeWeight(5, float.Parse(csvController.GetInstance().getString(date, 6)) * 100);
mesh.SetBlendShapeWeight(6, float.Parse(csvController.GetInstance().getString(date, 7)) * 100);
mesh.SetBlendShapeWeight(7, float.Parse(csvController.GetInstance().getString(date, 8)) * 100);
mesh.SetBlendShapeWeight(8, float.Parse(csvController.GetInstance().getString(date, 9)) * 100);
date++;
}
i++;
}
}
else
{
}
}
else
{
i++;
}
}
public void lipsync() // 方法用于播放音频同时开始进行口型同步
{
audio.Play();
lipsyncon = true;
}
public void ModelChange() // 更改口型变化模式
{
for (i = 0; i <= 8; i++)
{
mesh.SetBlendShapeWeight(i, 0);
}
if (model == false)
{
japanesemodel = true;
model = true;
}
else
{
japanesemodel = false;
model = false;
}
}
}
4.完成脚本后将,脚本拖拽到上步骤中添加的空Object上去,同时再在object上创建一个Audio Source组件,并将需要播放的wav放在组件中:
5.在上图中按到,Model Control脚本组件中的Mesh 和Audio已经被拖拽到位,实际上我们只需要按照如下方式操作即可:
6.为了更好的控制和方便测试,我们还需要创建两个Button,分别用于播放音频内容和切换模式:
7.两个按钮分别调用之前创建的ModelControl中的两个方法:
完成上述内容后,运行场景,分别点击两个按钮观察相关效果。
在Unity中我们主要通过对csv文件进行调用,同时按照帧率刷新csv的列数据,用于驱动模型的变形参数进行变化,从而完成口型匹配。
csv文件则是由上一篇文章中Lipsync工具进行离线分析后所得的结果。
Unreal中相关处理
Unreal中导使用相关分析的数据时,需要做预先处理,因为Unreal对导入的数据有一定的格式要求。
- 创建相关数据
- 导入已经分析完成的csv,导入后数据如下:
- 导入完成后数据准备就已经完成,下面需要创建相关蓝图,用于数据变化模型口型。
- 选择口型变形的模型,用其创建蓝图,并添加如下参数:
- 按照如下图中创建蓝图逻辑:
- 在此蓝图内容中我们主要处理了播放音频内容,和更改模式的选择,此时在上一步骤中设置的Bool型变量Japanese用于判断混合模式,是高度宽度模式还是元音分析模式,模式的不同导致的更改变形参数不同,效果也将不一样,采用Bool型变量来变化模式的选择:
- 而后我们采用如下的蓝图进行处理,当每帧调用时我们采用了序列的方法,执行两个内容,其中一条如下蓝图,另外一条下一步骤中进行说明。因为当我们的声音播放结束时我们不希望继续分析播放,因此我们采用了获取Audio是播放中来进行流程控制,当播放已经结束时将会执行下面的内容,首先设置CanUpdate为false,目的是为了进行csv数据调用是否执行,当声音播放结束时,我们无需在进行csv数据调用更改口型,因此更改CanUpdate的值。此外还进行了Date数据的重置。Date数据主要的目的是更新csv数据,因为我们的csv数据是按照一列的数据每帧刷新用于更新模型变形参数,因此设置一个Date用于更新这些列的数据,当声音播放完毕时,需要将Date还原。最后一个为宏Reset,主要作用为还原所有模型变形数据,蓝图如下2图:
- 每帧更新的另外一条逻辑如下,
- 首先是进行判断,参数为CanUpdate表示是否可以进行数据的分析:
- 当判断为true时,进行下一步内容获取csv数据,注意csv的列选择,我们采用了Date数据进行,主要是每一帧更新时我们需要不断替换列的数据以进行变形参数的变化:
- 而后的下一步骤我们进行判断,判断采用的分析模式,借由先前创建的Bool型参数Japanese,当为Ture和false时分别对应到元音分析:
- 在分析的内容中,我们要使用csv数据中的不同行,对应到不同的变形参数上,通过csv的数据,每帧更新相关的变形参数,而最后的设置Date数据+1的目的是为了每帧更新时,增加csv纵行的数据调用,以此来每帧按照纵列依次调用数据变化变形参数以匹配口型:
- 在此蓝图内容中我们主要处理了播放音频内容,和更改模式的选择,此时在上一步骤中设置的Bool型变量Japanese用于判断混合模式,是高度宽度模式还是元音分析模式,模式的不同导致的更改变形参数不同,效果也将不一样,采用Bool型变量来变化模式的选择:
按照上述内容完成后,我们将蓝图类拖入到关卡中,然后运行,分别按下1和2来测试最终效果。
如此一来我们的Lipsync离线工具解析说明就已经完成,我们通过介绍Lipsync的使用方法,进行wav声音文件的分析,而后介绍了如何使用批处理进行快速的wav文件分析。
当获得了我们的wav声音文件后,我们需要进行游戏内处理,以进行口型的匹配。因此我们介绍了如何在Unity中通过代码以及在Unreal中通过蓝图来实现上述效果。
Lipsync的离线分析模式主要用于直接离线进行wav的分析,分析后的数据文件由脚本自行处理,这种模式下我们可以应用在任何场合,而lipsync和ADX2的连用可以直接进行实时分析,效率更加优秀。