Unity项目捏脸解决方案BlendShape

Unity项目捏脸解决方案BlendShapes

引子

最近公司在开发一个捏脸的SDK,使用的核心功能就是使用Unity的SkinnedMeshRenderer上的BlendShapes来实现的。这个功能在Maya和3ds Max中都可以找到。

BlendShapes

BlendShapes是一种动画制作方式,与骨骼动画相比,它主要应用在很小的局部,比如角色的面部表情。
骨骼只适合控制整体的动作,但太局部的动作使用骨骼制作会非常繁琐,同时太多的骨骼对性能也有影响。
BlendShapes的原理很简单,就是在相邻两个网格间做插值运算,从一上形状融合到另一个形状;或者说是单个网格变形以实现许多预定义形状和任何数量之间组合的技术,在Maya/3ds Max中我们称它为变形目标,例如单个网格是默认形状的基本形状(例如无表情的面),并且基本形状的其他形状用于混合/变形,是不同的表达(笑、皱眉、闭合眼皮),这些被统称为混合形状或变形目标。所以美术只要制作若干个形状的模型即可,比摆骨骼要方便很多,特别是对于面部这种很局部的动作来说。
模型在导入时,Import BlendShapes选项默认是选中的。

具体使用

  1. 要在Maya或者3ds Max里面制作一个含有BlendShapes的形变动画Avator
  2. Avator导入到Unity中。
  3. 导入的模型上面有组件SkinnedMeshRenderer,面板上有BlendShapes参数,它的下面有一连串的数值可以手动或编码设置,来实现捏脸效果。

代码

Unity提供的一些相关接口(组件SkinnedMeshRenderer中):

  • void SetBlendShapeWeight(int index, float value):用于直接设置BlendShape的值,使用index按照面板上的自上而下顺序下标来索引设置的“部位”。
  • float GetBlendShapeWeight(int index):根据index获取对应的BlendShape值。
  • sharedMesh.blendShapeCount:组建中BlendShape的数量。
  • sharedMesh.GetBlendShapeName(int shapeIndex):根据index获取对应的BlendShape名称字符串。
  • sharedMesh.GetBlendShapeIndex(string blendShapeName):根据名称获取索引的下标(第几个)。

这里我只用了了几个,还有一些并没有使用测试过。

  • void AddBlendShapeFrame(string shapeName, float frameWeight, Vector3[] deltaVertices, Vector3[] deltaNormals, Vector3[] deltaTangents):添加一个新的BlendShape框架。
  • int GetBlendShapeFrameCount(int shapeIndex):返回BlendShape的帧数。
  • void GetBlendShapeFrameVertices(int shapeIndex, int frameIndex, Vector3[] deltaVertices, Vector3[] deltaNormals, Vector3[] deltaTangents):检索BlendShape框架的deltaVertices,deltaNormals和deltaTangents。
  • float GetBlendShapeFrameWeight(int shapeIndex, int frameIndex):返回BlendShape框架的权重。

之后下面是我使用的一个脚本,当使用的一个模型(FBX)下有好多分支模型节点时,使用这个脚本统一管理设置BlendShape

using UnityEngine;

public class BlendShapeModel : MonoBehaviour
{
    private SkinnedMeshRenderer[] skinnedMeshRenderers;
    private string[] m_bsNameAll;

    void Start()
    {//获取初始化
        skinnedMeshRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
        m_bsNameAll = new string[skinnedMeshRenderers.Length];
        for (int i = 0; i < skinnedMeshRenderers.Length; i++)
        {
            for (int j = 0; j < skinnedMeshRenderers[i].sharedMesh.blendShapeCount; j++)
            {
                m_bsNameAll[i] += skinnedMeshRenderers[i].sharedMesh.GetBlendShapeName(j) + ',';
            }
        }
    }

    /// <summary>
    /// 设置BlendShape值
    /// </summary>
    /// <param name="name">BlendShape的名字</param>
    /// <param name="value">BlendShape设置的值</param>
    public void SetBlendShapeValue(string name, float value)
    {
        for (int i = 0; i < m_bsNameAll.Length; i++)
        {
            if (m_bsNameAll[i].Contains(name))
            {
                int lenght = m_bsNameAll[i].IndexOf(name);
                int index = m_bsNameAll[i].Substring(0, lenght).Split(',').Length - 1;
                skinnedMeshRenderers[i].SetBlendShapeWeight(index, value);
            }
        }
    }
}

代码中,我是使用的名称检索对应的BlendShape,所以各个分支所对应的相同的BlendShape的名称要有相同的字符串,或一定的规律。

参考链接

  1. BlendShapes:https://www.jianshu.com/p/b8c1210dd12e
  2. Unity3D Blend Shape简析:https://www.jianshu.com/p/4ae6662a40df
  3. Unity工具类之BlendShape捏脸的实现:https://blog.csdn.net/u014361280/article/details/103929611
  • 8
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天富儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值