Unity的GPUSkinning进一步介绍

  大家好,我是阿赵。
  在几年前,我曾经写过一篇介绍GPUSkinning的文章,这么多年之后,还是看到不停有朋友在翻看这篇旧文章。今天上去GitHub看了一下,GPUSkinning这个开源的插件已经很久没有更新过了,还是停留在2017年的0.2.3版本。GPUSkinning的魅力在于可以在消耗比较低的情况下同屏显示很多个蒙皮动画的角色。
  看了一下之前写的文章,当时的我,水平也比较有限,所以只是简单的介绍了一下这个插件的用法。这么多年过去了,我感觉可以更深入的讨论一下这个插件的用法,还有它的实现原理。

一、使用说明和原理介绍

1、下载和安装

  由于最近上GitHub似乎有些困难,所以我这里再提供多一个能下载的地址:
https://kgithub.com/chengkehan/GPUSkinning/
在这里插入图片描述

  把插件下载到本地后解压缩,里面是一个Unity项目,也可以把GPUSkinning文件夹复制到自己的项目里面
在这里插入图片描述

2、采样动画数据

接下来要把我们基于Animator的动画转换成GPUSkinning插件使用的动画格式。

1.添加GPUSkinningSampler脚本

  把我们需要的带动画的预设文件拖到场景里面,然后添加一个GPUSkinningSampler脚本上去
在这里插入图片描述

  这里要注意一点,这个Animator组件里面的AnimatorController不能是AnimatorOverrideController,一定要是非Override的AnimatorController。不然会有报错提示。

2.必须设置的选项

在这里插入图片描述

  这里有几个东西需要设置,首先是这个动画角色的名称,然后是一个顶点受多少根骨骼影响,使用怎样的shader,设置根骨骼节点,选择是否生成新的Shader,最后指定动画片段。
需要注意的东西有:
  (1)顶点受多少根骨骼影响这个是可以后改的,不管选择多少根骨骼,工具都会导出uv2和uv3数据,其中uv2的数据是该顶点对应第1、2根骨骼的蒙皮权重,uv3是顶点对应第3、4根骨骼的蒙皮权重。区别只是在于自动选择或者生成的shader不同而已,shader可以后面在材质球直接选择或者修改的。
  (2)是否选择生成新shader,决定于你的模型是否有特殊的效果。如果有,比如原来的模型是使用了卡通渲染,或者pbr渲染之类的,在插件默认提供的shader里面找不到相同效果的shader可以选择时,就可以选择生成新shader,那么就会生成一个新的shader文件,里面的顶点程序是写好了蒙皮方法的,比如skin2或者skin3或者skin4之类,我们就可以修改这个shader,达到想要的效果
  (3)先设置动画片段的数量,然后动画片段需要手动拖进去,后面有两个选项,RootMotion是根节点是否移动,Individual Difference是该动画如果同时出现在多个个体身上时,是否需要有差异。之前的文章里面我说过GPUSkinning的其中一个缺点是在场景里面同一个角色播放同一个动作时会完全一样,这里的Individual Difference是为了解决这个问题,如果某个动作选择勾上这个选项,那么如果这个动作循环播放时,将会随机取一个数字,来错开动画播放的帧。

3.LOD选项

  在展开LOD选项后,可以配置LOD等级,这是一个可选项,如果本身模型有做多个不同等级的LOD模型,可以在这里设置
在这里插入图片描述

设置一个size,然后指定不同LOD等级使用的网格模型和距离。
在这里插入图片描述

4.预览和编辑

点击Preview/Edit按钮,可以打开预览和编辑的内容
在这里插入图片描述

这里可以选择动画,预览动画,并且增加动画事件
在这里插入图片描述

可以设置Bounds
在这里插入图片描述

最下面还有一个骨骼列表,前面可以勾选。这个勾选的作用,是可以保留某个骨骼节点。因为在通过GPUSkinning播放动画时,原有的骨骼和网格模型都不存在了,只有一个空节点。如果我们想做一些跟随功能,比如角色手上拿着的武器可以替换,那么我们就可以把手的骨骼勾上,这样播放的时候,会看到手的骨骼的节点,那么就可以把武器挂上去了。
在这里插入图片描述

5.采样动画

  在LOD下面,有个Step1:Play Scene的按钮。这其实就是提示我们如果要开始采样动画,就先点这个按钮运行场景。
在这里插入图片描述

  运行场景之后,这里会出现Step2,意思就是第二步,正式开始采样了。点击一下这个按钮
在这里插入图片描述

  插件就开始采样动画,并生成需要的文件了。实际上的过程,是逐个动画播放一次,然后每一帧获取骨骼的bindposes矩阵,这个矩阵是每根骨骼的位移旋转缩放,然后记录下来。
在这里插入图片描述

最后生成了这些文件
在这里插入图片描述

接下来说一下这些文件的作用
(1)anim属性文件
这个文件是导出的动画文件的总文件,里面包含了这个动画角色的大部分信息,比如名字、骨骼列表、动画列表、Bounds范围、根骨骼index、保存每帧动画的texture的宽高、Lod等级设置等。
在这里插入图片描述

(2)材质球文件
每个角色,都会生成一个材质球,如果在生成时勾选了新Shader,这里还会生成一个对应的Shader
在这里插入图片描述

(3)网格模型文件
包含了本身的角色的网格模型,还有lod等级里面的网格模型
在这里插入图片描述

仔细的看看信息,它包含了uv2和uv3的信息,上面介绍过,就是这个模型的顶点蒙皮权重
(4)还有一个包含Texture的byte文件。
  这个文件其实是一个贴图文件来的,不过是什么格式并不重要,因为里面的颜色值rgba,其实是每一帧每根骨骼的bindpose矩阵,3个顶点颜色代表了一个矩阵。
  之前在采样的时候说过,实际上是每个动画播放一次,然后每帧记录bindposes,这里就是把这些数据记录在rgba颜色里面。在需要播放动画的时候,从这些颜色值里面还原骨骼矩阵,然后结合着模型的uv2和uv3带有的蒙皮信息,就可以算出顶点在某一帧动画里面的位置了。

6.运行动画

  当生成完了需要的文件之后,就可以进行播放了
  先建一个空的GameObject在场景里,然后添加一个GPUSkinningPlayerMono脚本到GameObject上。
在这里插入图片描述

  把刚才生成的文件对应的拖入到GPUSkinningPlayerMono脚本里面
在这里插入图片描述

  当文件都拖入之后,就能看到场景里面出现了会动的模型了。
在这里插入图片描述

  这里可以选择需要播放的动画
在这里插入图片描述

  然后在运行的时候,可以通过脚本播放指定的动画:

player = GetComponent<GPUSkinningPlayerMono>().Player;
player.Play("Idle");

  观察一下GameObject节点,会发现刚才勾选了暴露的手部骨骼,在下面生成出来了。
在这里插入图片描述

  现在可以创建很多个这样的角色了同时运行。
在这里插入图片描述

二、原理的归纳

  刚才在介绍用法的时候,也介绍了一部分原理,这里归纳一下:

1、数据准备阶段

  1.通过uv2和uv3记录网格模型的顶点蒙皮信息
  2.采样的时候,每个动画播放一遍,然后每一帧记录骨骼的位移旋转缩放
  3.把动画里面每根骨骼的位移旋转缩放矩阵,通过rgba颜色,一个颜色记录四个数据,然后一个4x4矩阵原则上是需要4个像素的颜色记录的,但由于最下面一行数据是固定的0,0,01,所以只记录前面3行就够了,所以这个工具实际上只使用了3个像素颜色来记录一个骨骼的矩阵。这些像素色,记录在一张Texture2D上,然后通过Texture2D.GetRawTextureData方法获得byte[]并保存。

2、播放阶段

  1.通过一个控制器统一去加载和分配角色的动画资源,比如同一个角色,他使用的材质球其实是一样的,这样可以合并DrawCall
  2.通过Texture2D.LoadRawTextureData方法还原Texture2D数据,并还原每一帧每根骨骼的矩阵
  3.在需要播放动画的时候,通过uv2和uv3去得蒙皮信息,然后通过矩阵和权重计算出顶点的位置,达到了播放动画的目的。
  4.通过Renderer.SetPropertyBlock给Render设置不同的参数,就能得到差异化的播放动画了。

三、扩展思考

  类似GPUSkinning的功能,阿赵我其实自己也实现过,我实现过2次:
  一次是在以前做页游的时代,使用AS3编写过在GPU进行蒙皮骨骼动画播放的功能,骨骼动画的原理在之前的文章里面也介绍过,有兴趣的朋友可以去翻一下旧文章。
  另外一次是在Unity里面实现的,和GPUSkinning有点类似,也是通过贴图来记录动画关键帧信息,不过却有点不一样。
  我自己的实现方式具体是这样的:
  我在贴图里面记录的不是骨骼的信息,我也不记录顶点的蒙皮信息,我只关心顶点位置。
1、记录顶点的uv2

我把所有的顶点编了一个顺序,每个顶点的序号记录在uv2的u坐标里面,然后uv2的v坐标固定是0

Vector2[] uv2 = new Vector2[vertCount];
for(int i = 0;i<vertCount;i++)
{Vector2 tempUV = new Vector2((float)i /(float) (vertCount-1), 0);uv2[i] = tempUV;
}newMesh.uv2 = uv2;

  这里的意思是,只要根据uv2采样贴图,就能得到顶点对应某一帧的像素颜色,然后移动v坐标,就可以实现不同帧数的采样。
2、记录关键帧的时候,同样把所有动画播放一次,然后每一帧记录。不过记录的不是骨骼的bineposes,而是每个顶点在当前帧的实际位置。
这里会存在2个问题
  1.由于颜色的值只能是0-255,或者说是0-1范围,但顶点的坐标是没有范围的,而且可以是负数的。为了解决这个问题,所以需要先计算出所有顶点可能出现的最大和最小范围,记录两个Bounds,然后颜色值实际记录的,是当前顶点坐标在最大最小值之间的比例,所以肯定是0-1的。
  2.如果模型顶点很多,图片的宽度会变得特别的大,比如一个一万顶点的模型,动画总共有100帧,按照上面说的生成方式,图片的大小将会是10000x100,这样明显是不合理的,所以一般来说会设置一个最大宽度,比如2048,那么上面这个模型,将会生成一张2048*500的贴图。
3、播放动画的时候,就非常简单了,只需要指定当前需要播放第几帧,然后根据uv2的v坐标来上下移动贴图的采样范围,就能得到某一帧的颜色点,然后计算出顶点的实际坐标了。
4、由于只需要移动uv2的v坐标,所以控制方式可以很多样,单纯在shader里面用_Time来自动播放也行,传入Index指定播放某一帧也可以。
对比一下GPUSkinning,我这种方法有优点,也有缺点
优点:
  我这种计算方式,实际上是比GPUSkinning的计算量要小的,因为所有参数包括贴图都是直接放入材质球里面,基本上不需要C#的额外干涉就能播放,也不需要计算权重,直接设置顶点坐标就行。而且控制的手段也比较的灵活。
缺点:
  我这种计算方式,缺点也很明显。由于记录关键帧动画是根据顶点来记录的,所以如果顶点很多的模型,比如上万个顶点的模型,也是有的,需要记录的数据就会非常多。而GPUSkinning记录的是骨骼,一个角色模型骨骼再多也就几十到上百根骨骼而已,记录的数据就明显少很多。
  说了这么多,最后说句老实话,如果要我写一套像GPUSkinning一样完整的解决方案,阿赵我还是很难写出来的,因为要配套写很多相关的编辑器工具,对于我来说,工具量还是非常大的。

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

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

相关文章

云原生Kubernetes:kubectl管理命令

目录 一、理论 1.kubectl 管理命令 2.项目的生命周期 二、实验 1.kubectl 管理命令 2.项目的生命周期 三、总结 一、理论 1.kubectl 管理命令 &#xff08;1&#xff09;陈述式资源管理方法 kubernetes集群管理集群资源的唯一入口是通过相应的方法调用apiserver的接口…

复旦-华盛顿EMBA:AI时代掘金,科技进化里的挑战与机遇

如果从去年年底ChatGPT3.5发布算起&#xff0c;AI赛道的热度已经持续飙升了半年有余。      “AI的iPhone时刻”代表什么&#xff1f;AI驱动的商业时代已经到来&#xff1f;      我们能看到担忧、恐惧、憧憬&#xff0c;但唯独不缺狂飙突进、加速进化。人类制造AI&…

WordPress(4)关于网站的背景图片更换

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、更改的位置1. 红色区域是要更换的随机的图片二、替换图片位置三.开启随机数量四.结束前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也…

Hadoop的分布式文件存储系统HDFS组件的使用

Hadoop的第一个核心组件&#xff1a;HDFS&#xff08;分布式文件存储系统&#xff09; 一、HDFS的组成1、NameNode2、DataNode3、SecondaryNameNode4、客户端&#xff1a;命令行/Java API 二、HDFS的基本使用1、命令行操作2、Java API操作 三、HDFS的工作流程问题&#xff08;H…

Direct3D颜色

在Direct3D中颜色用RGB三元组来表示&#xff0c;RGB数据可用俩种不同的结构来保存&#xff0c;第一种是D3DCOLOR&#xff0c;它实际上与DWORD类型完全相同&#xff0c;共有32位&#xff0c;D3DCOLOR类型种的各位被分成四个8位项&#xff0c;每项存储了一种颜色分量的亮度值。 由…

【Hive SQL 每日一题】统计用户连续下单的日期区间

文章目录 测试数据需求说明需求实现 测试数据 create table test(user_id string,order_date string);INSERT INTO test(user_id, order_date) VALUES(101, 2021-09-21),(101, 2021-09-22),(101, 2021-09-23),(101, 2021-09-27),(101, 2021-09-28),(101, 2021-09-29),(101, 20…

C语言sizeof()计算空间大小为8的问题

在练习数据结构过程中&#xff0c;定义指针p&#xff0c;并且申请了10个char类型空间&#xff0c;但在计算p所指空间大小时候&#xff0c;发现了一些奇怪的现象。 #include <stdio.h> #include <stdlib.h>int main(){char s[12];printf("the size of memory …

Java反射:探索对象创建与类信息获取

文章目录 1. 对象的创建2. 类的初始化2.1 类的加载2.2 类的连接2.3 类的初始化 3. 反射是什么&#xff1f;4. 获取Class类对象4.1 使用类名.class4.2 使用对象的getClass()方法4.3 使用Class.forName() 5. 获取构造器对象5.1 使用getConstructors()和getDeclaredConstructors()…

pytorch代码实现之空间通道重组卷积SCConv

空间通道重组卷积SCConv 空间通道重组卷积SCConv&#xff0c;全称Spatial and Channel Reconstruction Convolution&#xff0c;CPR2023年提出&#xff0c;可以即插即用&#xff0c;能够在减少参数的同时提升性能的模块。其核心思想是希望能够实现减少特征冗余从而提高算法的效…

【探索Linux】—— 强大的命令行工具 P.8(进程优先级、环境变量)

阅读导航 前言一、进程优先级1. 优先级概念2. Linux查看系统进程3. PRI&#xff08;Priority&#xff09;和NI&#xff08;Nice&#xff09; 二、环境变量1. 概念2. 查看环境变量方法3. 环境变量的组织方式4.通过代码获取环境变量5. 环境变量的特点 总结温馨提示 前言 前面我们…

C++ - 多态的实现原理

前言 本博客主要介绍C 当中 多态语法的实现原理&#xff0c;如果有对 多态语法 有疑问的&#xff0c;请看下面这篇博客&#xff1a; 探究&#xff0c;为什么多态的条件是那样的&#xff08;虚函数表&#xff09; 首先&#xff0c;调用虚函数必须是 父类的 指针或 引用&#xf…

KT142C-sop16语音芯片ic的功能介绍 支持pwm和dac输出 usb直接更新内置空间

1.1 简介 KT142C是一个提供串口的SOP16语音芯片&#xff0c;完美的集成了MP3的硬解码。内置330KByte的空间&#xff0c;最大支持330秒的语音长度&#xff0c;支持多段语音&#xff0c;支持直驱0.5W的扬声器无需外置功放 软件支持串口通信协议&#xff0c;默认波特率9600.同时…

opencv旋转图像

0 、使用旋转矩阵旋转 import cv2img cv2.imread(img.jpg, 1) (h, w) img.shape[:2] # 获取图像的宽和高# 定义旋转中心坐标 center (w / 2, h / 2)# 定义旋转角度 angle 90# 定义缩放比例 scale 1# 获得旋转矩阵 M cv2.getRotationMatrix2D(center, angle, scale)# 进行…

比亚迪海豹:特斯拉强劲对手,瑞银拆解成本比同级车型低15%~35%

瑞银证券日前对中国电动车产品比亚迪海豹进行了拆解&#xff0c;发现海豹具有强大的成本优势&#xff0c;而这个优势主要来自于中国本土生产和国内完善的电动车供应链以及比亚迪的垂直整合体系和零部件高度集成性。比亚迪的整车成本比同级别竞争车型分别低15%至35%。 瑞银预测&…

【100天精通Python】Day55:Python 数据分析_Pandas数据选取和常用操作

目录 Pandas数据选择和操作 1 选择列和行 2 过滤数据 3 添加、删除和修改数据 4 数据排序 Pandas数据选择和操作 Pandas是一个Python库&#xff0c;用于数据分析和操作&#xff0c;提供了丰富的功能来选择、过滤、添加、删除和修改数据。 1 选择列和行 Pandas 提供了多种…

学习Bootstrap 5的第六天

目录 信息警告框 警告框 实例 警告框链接 实例 关闭警告框 实例 警告框动画 实例 按钮 按钮样式 实例 按钮轮廓 实例 ​编辑按钮尺寸 实例 块级按钮 实例 实例 活动/禁用按钮 实例 加载器按钮 实例 扩展小知识 信息警告框 警告框 警告框是使用 .aler…

ETCD详解

一、etcd概念 ETCD 是一个高可用的分布式键值key-value数据库&#xff0c;可用于服务发现。 ETCD 采用raft 一致性算法&#xff0c;基于 Go语言实现。 etcd作为一个高可用键值存储系统&#xff0c;天生就是为集群化而设计的。由于Raft算法在做决策时需要多数节点的投票&…

【算法】归并排序 详解

归并排序 详解 归并排序代码实现1. 递归版本2. 非递归版本 排序&#xff1a; 排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a; 假定在待排序的记录序列中&#xff0c;存在多个具有相…

eclipse进入断点之后,一直卡死,线程一直在运行【记录一种情况】

问题描述: 一直卡死在某个断点处&#xff0c;取消断点也是卡死在这边的进程处。 解决方式&#xff1a; 将JDK的使用内存进行了修改 ① 打开eclipse&#xff0c;window->preference->Java->Installed JREs&#xff0c;选中使用的jdk然后点击右侧的edit&#xff0c;在…

【算法】插入排序

插入排序 插入排序代码实现代码优化 排序&#xff1a; 排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a; 假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&…