Unity AnimationClip详解(2)——动画数据的优化

news2024/12/25 12:21:19

【内存优化】

首先要意识到运行时和编辑时的区别,当运行时和编辑时所需的数据相差不大时,我们用同一套数据结构即可,当两者差异较多或者数据量很大时,需要有各自的数据结构,这意味着在打包或构建时需要将编辑时数据转为运行时数据。

(所以Unity中的AnimationClip Curve数据不提供给非editor情况下使用)

(内存优化把握三个核心方向:一是内存中只存在当前或最近需要的资源及数据;二是需要的资源及数据在内存中仅存在一份;三是简化资源和数据的结构,运行时和编辑时的数据区分是方向1)

由前文可知,动画至少有30个骨骼节点,每个至少有9条Curve曲线,每个Curve曲线有60个KeyFrame,按照编辑时的Keyframe的结构,1s的动画至少需要30*9*60*8*4/1024 =506.25kb的内存

高品质的动作游戏的骨骼节点会更多,1s内的动画数据占用的内存会超过506.25kb。

一般而言,内存中存在的所有动画数据时长加起来超过30分钟很正常,那么占用内存至少为506.25*60*30/1024 = 889.89MB

这么大的内存在移动端是不可接受的。

根据计算公式,我们可以从多个方面优化内存。

优化关键帧结构

在运行时我们只需要知道曲线函数即可,编辑时点的数据可直接转为函数参数,从前文可知函数,可以有:

  • 水平线(即常量),点的值
  • 直线,y= ax+b ,两个值
  • 三次多项式,y= ax^3 + bx^2 + cx + d 四个值
  • 二次贝塞尔曲线  B(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2,展开后为y= ax^2 + bx + c,三个值
  • 三次贝塞尔曲线  B(t) = (1 − t)^3 *P0 + 3t (1 − t)^2 *P1 + 3t^2 (1 − t)*P2 + t3* P3,展开后为y= ax^3 + bx^2 + cx + d 四个值
  • 三次埃尔米特曲线,展开后为y= ax^3 + bx^2 + cx + d 四个值

可以看到,他们都是同样的形式,不必再区分具体的类型,只要有四个参数值即可。

那么运行时关键帧的数据结构A为:

    ///Struct KeyFrame{
    ///     float a
    ///     float b
    ///     float c
    ///     float d
    ///}

按照这个优化,内存将降低为原来的一半,为444.95MB。

接下来还按照核心方向3来优化:

uint类型有32位,分成4份,每份有8位,我们用高位标记其实小数点后几位,剩余7位表示参数值,可表示的最大精度为0.0000001,这个精度基本够了

按照这个方式运行时关键帧结构B为:uint keyFrame

内存将降低为原来的1/8,为:111.24MB(下文以这个为准)

优化曲线结构

曲线核心数据是关键帧数据的组合,优化要依靠不同关键帧之前的关联(类似数据压缩中上下文联系)来简化数据(核心方向3),有以下关联:

1.如果曲线中所有关键帧都是常量,我们用一个数据就可以,在关键帧数据结构A中,内存占用降低为原来的1/(60*4);在关键帧数据结构B中,内存占用降低为原来的1/60。例如:盆骨节点的部分曲线基本都是常量

2.如果曲线中有部分连续的常量,我们可以需要标识出从第几帧到第几帧是常量,这是不用一个数组表示了,需要用一个类封装,这也是划算的

3.如果曲线中所有关键帧都是直线,在关键帧数据结构A中,可以去掉c、d,内存占用降低为原来的1/2;在关键帧数据结构B中,可以用short类型,内存占用降低为原来的1/2

以上优化都属于核心方向3中的方法1:转换数据结构,其特点是不损失数据精度

在允许精度损失的情况下,我们有了方法2:朝着符合转换数据结构的情况精简数据

针对关联1:我们可以设置一个阈值N,凡是变化在阈值内的数据,都认为是常量

在游戏行业,动画数据生成的方式一般有三种:1.美术在3D软件中手K 2.动捕 3.AI生成。其中动捕或AI生成会产生较多抖动细节,都可以去掉以生成更多的常量。

针对关联2:我们需要在曲线的所有位置都检测是否存在常量,一些斜率很小的直线可以简化为常量

针对关联3:有些其他曲线完全很小,可以近似简化为直线,以尝试获得更多的直线

(有些算法常识,你应该可以知道,上述所有阈值,都可以做自适应。

理论上,自适应是针对不同情况的,对当前曲线有自适应值,对不同情况下生成的近似曲线也要有不同的自适应值;对不同的肢体,例如手部、腿部、面部的自适应值不同,越靠近根骨骼,所需精度越高,允许的误差阈值越小。

实际上,为了简化,可能都是一样的自适应值。)

允许有精度损失时,还有其他关联:

4.连续多个关键帧数据可以通过一条曲线拟合,实际上这种情况是很常见的,只要不是突兀的变化,时间是在1/60s这样小的时间尺度下,多个连续帧是按照同一规律渐变的,可以用同一条曲线拟合。

注意,我们并不是一次性拟合整条曲线,而是对曲线分段拟合

综合以上关联,优化后的曲线结构中,关键帧数据不再是数组,而是一个封装关键帧数据的类或结构体。在Unity中,表现为IntPtr m_Ptr。

一般来说,常量占到曲线的60%-80%,直线占比为20%~30%,其他各类曲线占比20~30%,假设30%关键帧数据可以做拟合。

做保守估计,常量取60%,直线取20%,其他各类曲线取20%,优化后的内存为:111.24*60%/60 + 111.24*20%/2 + 111.24*20%*(60 - 60*30%+1 )/60 = 28.181MB

优化关键帧数量

有些动作变化简单,例如walk、run等,实际上不需要60帧,2D动漫也常用做这样的减帧处理。(核心方向3的方法3:减少数据量)

因为大部分动作都是平滑的,我们可以预测下一帧的数据,从而减少帧数。曲线拟合也可以看作是减帧,与预测不同的是,曲线拟合有精度要求,无论动画正放还是倒放都无影响,且不需要前置数据。

而预测首先的有几帧的前置数据,由于是从前到后预测,动画不能倒放,预测最好是完全准确的,在阈值内有偏差也可,超过阈值,记录一个delta即可校正。

假设通过上述方式,可以将平均帧数降低为40帧,那么优化后的内存为:28.181 * 40 / 60 = 18.787MB

优化曲线数量

上述的优化都是基于数据本身做优化,并没有考虑到数据所在的场景。在动作中,节点位置、朝向、缩放的三条曲线之前是有关联的,我们完全可以将其做进一步的封装。

例如:xz可能共同绕着y做同样规律的变化,或者xy绕着z做同样规律的变化,考虑人体结构和肢体动作,这是很常见的,减少30%-50%的数据是可能的。例如Unity内部的QuaternionCurve,Vector3Curve

对于缩放,一般情况下角色没有缩放,可以直接去掉缩放曲线。

假设去掉Scale曲线,并做30%优化,那么优化后的内存为:18.787 * (6/9)*70% =  8.767MB

优化节点数量

角色骨骼节点数量在项目之初就确定好的,不会随意更改,这里要结合核心方向1来做优化。

更多的骨骼节点是为了走更精致、品质更高的动作,在不在视野内、或距离视野很远的角色而言,精致的动作也看不到,角色不需要或仅需要很少的动画,也相当于一些节点的数据直接可以省略掉,不用在内存中存在。

假设只能优化10%的节点点,那么优化后内存为:8.767 * 90% = 7.908MB

综上,内存可以从889.89MB优化到7.908MB,至少可以优化88%

压缩优化

按照数据压缩中的方法,将动画数据做压缩(注意选择高性能的压缩方法),使用时再解压数据。

加载优化

主要是按照方向1别把不需要的动画数据加载到内存中。

unity中的优化设置

Unity针对AnimationClip提供三种压缩格式:

1.Off——不做压缩处理,动画中每一帧都生成关键帧

2.Keyframe Reduction——Stream格式存储,使用关键帧缩减算法(简单来讲,就是对去除关键帧前后的曲线进行比较,如果对应的曲线值的差小于容错值/误差宽容度,则去掉关键帧)

3.Optimal——Unity会使用启发式算法,从而决定使用Keyframe Reduction算法进行压缩(Stream格式存储),或者使用Dense格式压缩存储动画曲

Stream格式可以认为是带有曲线的;Dense格式是存储所有关键帧数据,用线性插值,可以看作是直线的,内存占用比Stream少

Inspector上可以看到不同类型曲线占据的大小

Rotation/Position/Scale Error是压缩时的阈值

可以对不同的动画资源选择合适的压缩格式和阈值

包体优化

一般来说,游戏内所有的动画数据时长加起来有6个小时比较正常,内存优化的某些方式,也会减少包体大小。

同时,压缩优化是必须的,在内存中的动画数据可以是非压缩的,在硬盘上的动画数据一般是压缩后的,读取文件时要先解压。

【性能优化】

这里的优化仅针对AnimationClip,由于解压、采样、计算都由引擎内部做处理,在不改源码的情况下,可以做的不多。

加载优化

在合适的时机做加载,以减少峰值卡顿等

缓存友好

 让数据的排布更符合读取的顺序,减少CPU cache miss的情况,例如由三条曲线,四个关键帧的数据,一般都是这样的:

    /// 曲线1:关键帧1 关键帧2 关键帧3 关键帧4
    /// 曲线2:关键帧1 关键帧2 关键帧3 关键帧4
    /// 曲线3:关键帧1 关键帧2 关键帧3 关键帧4
    /// 曲线4:关键帧1 关键帧2 关键帧3 关键帧4

改成:

    /// 关键帧1:曲线1 曲线2 曲线3 曲线4
    /// 关键帧2:曲线1 曲线2 曲线3 曲线4
    /// 关键帧3:曲线1 曲线2 曲线3 曲线4
    /// 关键帧4:曲线1 曲线2 曲线3 曲线4

由于各类压缩和优化内存的方式,缓存友好的方式改动难度很大

Job计算

多个不同的AnimationClip的数据的采样、计算可以放在Job中进行

采样降频

动画数据采样是指,输入一个时间,从曲线中得到一个值,每秒要进行很多次这样的采样。一般来说,会有预设的固定值,或者按照Update的频率采样。

但我们可以结合业务实际去调整采样的频率,类似常见物体LOD的概念,我们可以根据角色和相机的距离来设置动画的采样频率。

在Unity中,如果项目使用了Playable做动作系统,可以使用Playable.Evaluate来手动更新,降低频率

【加载优化】

加载关乎到内存和性能,这里拿出来单独说,从加载系统的交互看,动画资源和其他资源没什么区别,以下加载优化的方式适用其他任何资源的加载优化

内存上

按需加载

动态卸载

性能上

提前加载:有些基础性的动画资源是必然会存在的,可以在切场景时提前加载到内存中

设置加载优先级:动画资源的加载优先级设置高些,让加载系统优先加载动画

异步加载:动画资源从同步加载改成异步加载,且需要加上时间限制,固定多少帧内必须加载完成

分帧请求加载:一次性需要加载的动画资源较多,分成多帧去请求

预测加载:可以在逻辑上预测哪些动画资源可能在接下来需要加载,提前做加载,以避免集中加载导致卡顿

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

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

相关文章

Android Framework关闭触摸振动

文章目录 手势上滑时振动代码performHapticFeedback作用和意义 触摸振动开关设置Framework关闭触摸时振动 手势上滑时振动代码 安卓手机由底部往上滑时,会有震动,然后进入Recents多任务,其触发震动调用的代码 packages/apps/Launcher3/quick…

LCD手机屏幕高精度贴合

LCD手机屏幕贴合,作为智能手机生产线上至关重要的一环,其质量直接关乎用户体验与产品竞争力。这一工艺不仅要求屏幕组件间的无缝对接,达到极致的视觉与触觉效果,还需确保在整个生产过程中,从材料准备到最终成品&#x…

robots协议ctf

robots协议 Robots协议,全称为“网络爬虫排除标准”(Robots Exclusion Protocol),是互联网上用于指导搜索引擎蜘蛛如何抓取和访问网站的一种协议。网站可以通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,从而保护…

神经架构搜索:自动化设计神经网络的方法

在人工智能(AI)和深度学习(Deep Learning)快速发展的背景下,神经网络架构的设计已成为一个日益复杂而关键的任务。传统上,研究人员和工程师需要通过经验和反复试验来手动设计神经网络,耗费大量时…

怿星科技薛春宇丨智能汽车软件研发工具链国产化的挑战和探索

2024年7月25日,由上海良益企业管理咨询有限公司主办的“2024域控制器技术论坛“在上海成功举办,十位嘉宾做了精彩分享。“整零有道”将陆续刊出部分演讲的文字实录,以飨读者。 本期刊出怿星科技副总经理薛春宇的演讲实录:智能汽车…

react-signature-canvas 实现画笔与橡皮擦功能

react-signature-canvas git 地址 代码示例 import React, { Component } from react import { createRoot } from react-dom/clientimport SignaturePad from ../../src/index.tsximport * as styles from ./styles.module.cssclass App extends Component {state { trimmed…

sheng的学习笔记-AI基础-正确率/召回率/F1指标/ROC曲线

AI目录:sheng的学习笔记-AI目录-CSDN博客 分类准确度问题 假设有一个癌症预测系统,输入体检信息,可以判断是否有癌症。如果癌症产生的概率只有0.1%,那么系统预测所有人都是健康,即可达到99.9%的准确率。 但显然这样的…

多款云存储平台存在安全漏洞,影响超2200万用户

据苏黎世联邦理工学院研究人员Jonas Hofmann和Kien Tuong Turong的发现,端到端加密(E2EE)云存储平台存在一系列安全问题,可能会使用户数据暴露给恶意行为者。在通过密码学分析后,研究人员揭示了Sync、pCloud、Icedrive…

方形件排样优化与订单组批问题探析

方形件排样优化与订单组批问题是计算复杂度很高的组合优化问题,在工业工程中有很广泛的应用背景。为实现个性化定制生产模式,企业会选择订单组批的方式,继而通过排样优化实现批量切割,加工完成后再按照不同客户需求进行分拣&#…

洛谷 P1226:【模板】快速幂

【题目来源】https://www.luogu.com.cn/problem/P1226【题目描述】 给你三个整数 a,b,p,求 a^b mod p。【输入格式】 输入只有一行三个整数,分别代表 a,b,p。【输出格式】 输出一行一个字符串 a^b mod ps&a…

多线程——线程池

目录 前言 一、什么是线程池 1.引入线程池的原因 2.线程池的介绍 二、标准库中的线程池 1.构造方法 2.方法参数 (1)corePoolSize 与 maximumPoolSize (2)keepAliveTime 与 unit (3)workQueue&am…

GPT-4o 和 GPT-4 Turbo 模型之间的对比

GPT-4o 和 GPT-4 Turbo 之间的对比 备注 要弄 AI ,不同模型之间的对比就比较重要。 GPT-4o 是 GPT-4 Turbo 的升级版本,能够提供比 GPT-4 Turbo 更多的内容和信息,但成功相对来说更高一些。 第三方引用 在 2024 年 5 月 13 日&#xff0…

HTB:Blocky[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机进行端口扫描 再次使用nmap对靶机开放端口进行脚本、服务信息扫描 对FTP服务版本:ProFTPD_1.3.5进行漏洞扫描 对SSH服务版本:OpenSSH 7.2p2进行漏洞扫描 使用浏览器访问靶机80端口 使用浏览器访问U…

信息搜集-域名信息收集

1.1 域名信息收集 WHOIS查询: 通过WHOIS查询可以快速得到域名的IP段、DNS解析、注册时间、地址等信息,或许运用合理可以巧妙的绕过CDN。备案信息收集: 网站备案信息收集更加方便定位资产到具体的企业名称、ICP备案号、备案人名称、公司、所处…

图片写入GPS经纬高信息

近期项目中需要往java平台传输图片,直接使用QNetworkAccessManager和QHttpMultipart类即可,其他博文中有分享。 主要是平台接口对所传输图片有要求:需要包含GPS信息(经度、纬度、高度)。 Qt无法直接实现,…

2003年秋季我给哈工大数学研究生写的讲义

我写的没出版讲义共有278页,书末参考文献 我写的讲义书末索引,冯克勤的书《代数数论》书末没有索引,陆洪文、李云峰的书《模形式讲义》书末也有索引 我写的讲义自制的封面 (对数学的研究我的经验是,跟其他科学类似&am…

「Qt Widget中文示例指南」如何实现半透明背景?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 本文将为大家展示如…

Java应用程序的服务器有哪些

1.Tomcat、Jetty 和 JBoss 区别? Apache Tomcat、Jetty 和 JBoss都是用于部署Java应用程序的服务器,它们都支持Servlet、JSP和其他Java EE(现在称为Jakarta EE)技术。尽管它们有一些相似的功能,但它们之间还是存在一些…

Mysql在线修改表结构工具gh-ost使用说明及实践

本文内容较多,篇幅较长,若不想了解ghost原理,几种模式的介绍以及具体的验证过程,可直接跳到‘四 gh-ost使用总结’查看简洁版使用说明。 一 gh-ost使用场景 生产环境当有关于一个大表的大操作时(比如select count一个大表)&…

沈阳乐晟睿浩科技有限公司抖音小店领域的强者

在当今数字化浪潮的推动下,电子商务以其便捷性、高效性和广泛的覆盖面,成为了推动经济发展的新引擎。而抖音小店,作为短视频平台上的新兴电商形态,更是凭借其庞大的用户基础、精准的内容推送机制以及独特的购物体验,迅…