unity - Blend Shape - 变形器 - 实践

news2024/11/23 22:03:07

文章目录

  • 目的
  • Blend Shape 逐顶点 多个混合思路
  • Blender
  • 3Ds max
  • Unity 中使用
  • Project


目的

拾遗,备份


Blend Shape 逐顶点 多个混合思路

blend shape 基于: vertex number, vertex sn 相同,才能正常混合、播放
也就是 vertex buffer 的顶点数量一样,还有 triangles 的 index 要一致

这样 blend shape 才能逐个顶点计算

计算公式:使用一张大佬整理的图,大佬的文章:BlendShapes基础与拓展练习(面捕与物体变形)
在这里插入图片描述


Blender

Shift+A 新建一个 sphere
在这里插入图片描述

选中

在这里插入图片描述

Tab 进入 Editor Mode,并且在 Data 页签属性的 Shape Keys 添加对应的 blend shape 状态
在这里插入图片描述
在这里插入图片描述

调整好每一个 Shape Keys (或是叫:blend shape) 的顶点位置
然后再 Object Mode 下,我们可以选中对应的 Shape Keys 然后调整 value 查看变形结果
请添加图片描述

最终我们尝试 K帧动画来查看 多个 shape keys 混合控制的情况
请添加图片描述


3Ds max

interlude _1 : working on Yui-chan’s face morphing. - 3Ds max 中的演示 二次元 脸部表情 FFD


Unity 中使用

先在 blender 导出 fbx
在这里插入图片描述

将 fbx 模型拖拽到 hierarchy
在这里插入图片描述

尝试拖拉 inspector 中的 blend shape 拉杆,即可查看效果
请添加图片描述

所以我们写脚本控制 blend shape 混合拉杆即可达到我们各种表情的混合控制
请添加图片描述

在原来 blendshape_1 基础上在混合 blendshape_2
请添加图片描述

blendshape_1 和 blendshape_2 一起播放
请添加图片描述

然后可以尝试看一下 blendshape_1 和 blendshape_2 不同速率的控制混合的情况
这里使用 pingpong 算法

请添加图片描述

下面是测试 csharp 脚本

// jave.lin 2023/10/07 测试 blend shape

using System.Collections;
using UnityEngine;

public class TestingBlendShape : MonoBehaviour
{
    public SkinnedMeshRenderer skinnedMeshRenderer;
    private int _BlendShape_1_IDX = -1;
    private int _BlendShape_2_IDX = -1;

    private IEnumerator _couroutine_blendShape1;
    private IEnumerator _couroutine_blendShape2;
    private IEnumerator _couroutine_pingpong;

    private void OnDestroy()
    {
        StopAllCoroutines();
    }

    private void Refresh()
    {
        if (skinnedMeshRenderer != null)
        {
            _BlendShape_1_IDX = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex("BlendShape_1");
            _BlendShape_2_IDX = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex("BlendShape_2");
        }
    }

    public void ToBlendShape_1()
    {
        Refresh();

        if (_couroutine_blendShape1 != null)
        {
            StopCoroutine(_couroutine_blendShape1);
        }
        StartCoroutine(_couroutine_blendShape1 = Play_ToBlendShape(_BlendShape_1_IDX, 100.0f));
    }

    public void ToBlendShape_2()
    {
        Refresh();

        if (_couroutine_blendShape2 != null)
        {
            StopCoroutine(_couroutine_blendShape2);
        }
        StartCoroutine(_couroutine_blendShape2 = Play_ToBlendShape(_BlendShape_2_IDX, 100.0f));
    }

    public void ToBlendShape_1_2()
    {
        Refresh();

        if (_couroutine_blendShape1 != null)
        {
            StopCoroutine(_couroutine_blendShape1);
        }
        StartCoroutine(_couroutine_blendShape1 = Play_ToBlendShape(_BlendShape_1_IDX, 100.0f));
        if (_couroutine_blendShape2 != null)
        {
            StopCoroutine(_couroutine_blendShape2);
        }
        StartCoroutine(_couroutine_blendShape2 = Play_ToBlendShape(_BlendShape_2_IDX, 100.0f));
    }

    public void ToPingPong_BlendShape_1_2()
    {
        Refresh();

        if (_couroutine_pingpong != null)
        {
            StopCoroutine(_couroutine_pingpong);
        }
        StartCoroutine(_couroutine_pingpong = PingPong_BlendShape());
    }

    public void StopAll()
    {
        StopAllCoroutines();
    }

    public void ResetAll()
    {
        if (skinnedMeshRenderer != null)
        {
            var count = skinnedMeshRenderer.sharedMesh.blendShapeCount;
            for (int i = 0; i < count; i++)
            {
                skinnedMeshRenderer.SetBlendShapeWeight(i, 0.0f);
            }
        }
    }

    private IEnumerator Play_ToBlendShape(int idx, float to_val)
    {
        to_val = Mathf.Clamp(to_val, 0.0f, 100.0f);
        var start_val = skinnedMeshRenderer.GetBlendShapeWeight(idx);
        var cur_val = start_val;

        while (cur_val < to_val)
        {
            cur_val = Mathf.MoveTowards(cur_val, to_val, (to_val - start_val) * Time.deltaTime);
            skinnedMeshRenderer.SetBlendShapeWeight(idx, cur_val);
            yield return null;
        }

        Debug.Log($"play to blend shape [{idx}] : [{to_val}] complete!");
    }

    private IEnumerator PingPong_BlendShape()
    {
        var now_time = Time.time;

        while (true)
        {
            var _time = Time.time - now_time;
            var weight1 = Mathf.PingPong(_time * 200f, 100f);
            var weight2 = Mathf.PingPong(_time * 50f, 100f);
            skinnedMeshRenderer.SetBlendShapeWeight(_BlendShape_1_IDX, weight1);
            skinnedMeshRenderer.SetBlendShapeWeight(_BlendShape_2_IDX, weight2);
            yield return null;
        }
    }
}


Project

个人备份用

  • blender 工程: TestingBlenderShapeKeys.blend
  • unity 工程: TestingBlendShape_BRP_2020.3.37f1.rar

  • 百人计划-BlendShapes基础(物体变形与面捕应用)
  • google : how to implementing the blend shape in blender
  • google : how to create shape keys animation in blender
    • Shape Key / Blendshape Hacks to easily create expressions and actions for your avatar
    • Blender 2.8 Shapekeys and Morphing
    • How to Add Shape Keys in Blender
  • 形态键
  • 在Unity中实现BlendShape表情和骨骼动画混合的实践 - 讲得挺不错
  • BlendShapes基础与拓展练习(面捕与物体变形) - 讲得挺不错
  • GDC2011: Fast and Efficient Facial Rigging
  • GDC jeremy_ernst_fastandefficietfacialrigging.pdf
  • 技术美术百人计划-美术 3.5 BlendShape基础 笔记
  • 3.5 BlendShapes基础
  • 百人计划-BlendShapes基础(物体变形与面捕应用)
  • 【技术美术百人计划】美术 3.5 BlendShape基础
  • Unity通过导入器优化动画关键帧数据 - 删除仅仅只有 blend shape 的动画的其他骨骼信息,优化性能
  • interlude _1 : working on Yui-chan’s face morphing. - 3Ds max 中的演示 二次元 脸部表情 FFD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1185088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CocosCreator让一个物体跟随鼠标移动(两种方式 本地坐标系和世界坐标系)

在 Cocos Creator 3.x 游戏运行时显示的画布大小就是屏幕区域&#xff0c;屏幕坐标是从画布的左下角为原点开始计算 在 Creator 3.x 里&#xff0c;屏幕和 UI 是完全区分开的&#xff0c;用户可以在没有 UI 的情况下点击屏幕获取触点信息。因此&#xff0c;获取屏幕触点&#…

Jmeter工具二次开发

一、JMeter 二次开发方向 1、函数开发&#xff0c;主要为JMeter 函数库 2、插件开发&#xff0c;一般主要做取样器开发 3、基于执行引擎开发&#xff0c;有效解决单独开发的测试平台或工具中&#xff0c;底层执行引擎开发相对复杂、周期长的问题&#xff0c;利用 JMeter 执行…

分享vmware和Oracle VM VirtualBox虚拟机的区别,简述哪一个更适合我?

VMware和Oracle VM VirtualBox虚拟机的区别主要体现在以下几个方面&#xff1a; 首先两种软件的安装使用教程如下&#xff1a; 1&#xff1a;VMware ESXI 安装使用教程 2&#xff1a;Oracle VM VirtualBox安装使用教程 商业模式&#xff1a;VMware是一家商业公司&#xff0c;而…

数据结构: 哈希桶

目录 1.概念 2.模拟实现 2.1框架 2.2哈希桶结构 2.3相关功能 Modify --Insert --Erase --Find 2.4非整型数据入哈希桶 1.仿函数 2.BKDR哈希 1.概念 具有相同地址的key值归于同一集合中,这个集合称为一个桶,各个桶的元素通过单链表链接 2.模拟实现 2.1框架 a.写出…

oracle数据导出exp导入imp

Oracle的exp/imp命令用于实现对数据库的导出/导入操作&#xff1b; exp命令用于把数据从远程数据库服务器导出至本地&#xff0c;生成dmp文件&#xff1b; imp命令用于把本地的数据库dmp文件从本地导入到远程的Oracle数据库。 一、获取帮助信息 exp/imp helpy 二、数据导出 1…

打破边界,一触即达——全新跨境电商业态,一键开启全球贸易新时代!

随着全球电子商务的飞速发展&#xff0c;跨境电商已成为连接国内外市场的桥梁。为了满足商家日益增长的海外拓展需求&#xff0c;我们重磅推出跨境电商源码商城&#xff0c;融合商家一键铺货、代理商后台、供货商后台、商品采集、短视频、直播、社交、分销、积分、多语言、国际…

阿里云双11优惠:云服务器1年99元,新老同享,续费同价!

阿里云2核2G3M带宽99元服务器新老用户同享&#xff0c;续费不涨价&#xff0c;99元即可续费&#xff0c;可以续费到2027年&#xff0c;相当于396元买4年&#xff0c;阿里云百科aliyunbaike.com来详细说下阿里云99元服务器配置、购买条件、优惠价格和续费攻略&#xff1a; 阿里…

js获取地址中携带的省市区

match() 方法可在字符串内检索指定的值&#xff0c;或找到一个或多个正则表达式的匹配。 match() 方法将检索字符串 String Object&#xff0c;以找到一个或多个与 regexp 匹配的文本。这个方法的行为在很大程度上有赖于 regexp 是否具有标志 g。如果 regexp 没有标志 g&#x…

图形界面应用案例——关灯游戏(以及扩展)(python)

7.8 图形界面应用案例——关灯游戏 题目: [案例]游戏初步——关灯游戏。 关灯游戏是很有意思的益智游戏,玩家通过单击关掉(或打开)一盏灯。如果关(掉(或打开)一个电灯,其周围(上下左右)的电灯也会触及开关,成功地关掉所有电灯即可过关。 图7-43 关灯游戏运行效…

阿里云服务器登录、安装MySql、配置Python、GO环境

1、刚购买的云服务如何登录 刚购买的ECS没有默认密码&#xff0c;需要先设置一下登录的密码。选中实例&#xff0c;右上角的全部操作-->重置实例密码 如果想通过SSH登录&#xff0c;则一定要勾选开启 修改之后ssh root你机器的ip&#xff0c;输入密码就可以愉快的开始你的操…

在Ubuntu下安装Redis

文章目录 前言一、配置JAVA运行环境二、Ubuntu下安装Redis1.安装c语言编译环境2.下载解压Redis3.make编译4.启动Redis4.运行Redis 三、性能测试总结 前言 版本 jdk版本&#xff1a;jdk-17_linux-x64_bin 地址&#xff1a;https://www.oracle.com/cn/java/technologies/downloa…

chatglm3-6b部署及微调

chatglm3-6b部署及微调 modelscope: https://modelscope.cn/models/ZhipuAI/chatglm3-6b/filesgithub: https://github.com/THUDM/ChatGLM3镜像: ubuntu20.04-cuda11.8.0-py38-torch2.0.1-tf2.13.0-1.9.4v100 16G现存 单卡 安装 软件依赖 pip install --upgrade pippip ins…

MySQL基础架构详解

概述 我们学习东西&#xff0c;都不应该是先去了解细节&#xff0c;而是应该窥其全貌&#xff0c;这样才能从高纬度去理解问题&#xff0c;同样我们学习mysql也是一样的&#xff0c;我们应该先了解整个mysql架构&#xff0c;及来龙去脉&#xff0c;才能更好的掌握它。下面我们开…

阿里云 :推出通义大模型编码助手产品【通义灵码】

本心、输入输出、结果 文章目录 阿里云 &#xff1a;推出通义大模型编码助手产品【通义灵码】前言通义灵码简介主要功能主要功能点 支持的语言和 IDEjetbrains IDEA 安装计费相关弘扬爱国精神 阿里云 &#xff1a;推出通义大模型编码助手产品【通义灵码】 编辑&#xff1a;简简…

java实现wav的重采样

在处理一些用户上传的音频的时候&#xff0c;往往根据用户的设备不通&#xff0c;文件格式难以统一&#xff0c;尤其是涉及到算法模型相关的&#xff0c;更是令人头疼&#xff0c;这里提供两种思路解决这个问题。 不借助三方库 这种采用的是javax.sound.sampled下的包来实现&a…

数据结构之顺序表的实现(详解!附完整代码)

线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构 常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线。但是在物理结…

十五、W5100S/W5500+RP2040树莓派Pico<TFTP Client>

文章目录 1 前言2 简介2 .1 什么是TFTP&#xff1f;2.2 TFTP的优点2.3 TFTP和FTP对比2.4 TFTP应用场景 3 WIZnet以太网芯片4 ARP网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前言 一般来说&#xff0…

金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件

文章目录 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件背景说明业务需求格式BOS配置 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件 背景说明 序列号档案是基础资料&#xff0c;资料里…

delphi程序启动时带参数运行的例子

这里有一个坑&#xff0c;就是参数会减少一个 //需要引用这个单元 uses shellapiprocedure TForm1.Button5Click(Sender: TObject); varParams: string; begin //由于第三个参数不会显示&#xff0c;需要额外的多补充一个参数&#xff0c;而且第一个参数会变成程序的运行路径P…

什么是超级托斯卡纳葡萄酒?

超级托斯卡纳葡萄酒通常被认为是在托斯卡纳用国际葡萄品种制成的葡萄酒&#xff0c;如赤霞珠、品丽珠或梅洛&#xff0c;而不是传统的托斯卡纳葡萄桑娇维塞。来自云仓酒庄品牌雷盛红酒分享这些葡萄酒可能包含一些桑娇维塞&#xff0c;但这通常不是混合中的主要葡萄。这些大胆的…