GPU ECS AnimationBaker 烘焙动画方案原理
目录方案概述烘焙阶段离线骨骼矩阵纹理布局CPU 运行时工作数据如何传给 GPUGPU 运行时工作加权蒙皮数学原理挂载道具剑/枪项目实例Zombunny1. 方案概述核心思想将传统Animator SkinnedMeshRenderer的骨骼动画离线烘焙为纹理数据运行时CPU只计算当前该查纹理的哪一行、帧间插值多少、多路动画如何混合约 64 字节/角色/帧GPU查纹理拿骨骼矩阵在 Vertex Shader 里完成顶点蒙皮三阶段分工阶段执行时机做什么数据量烘焙Editor 一次性采样动画 → 算蒙皮矩阵 → 写纹理 UV 权重大MB 级静态CPU每帧推进时间 → 算frameIndex 混合参数小~64B/实例/帧GPU每顶点并行查纹理 → LBS 蒙皮 → 输出变形顶点并行与本项目的关系ShaderAssets/Res/Shader/EnemyGpu.shadergraph、PlayerGpu.shadergraph烘焙资源Assets/Res/Prefabs/EntityAnimG/BakedAssets_*Gpu/运行时 SystemGpuEcsAnimatorSystem、GpuEcsAnimatedMeshSystemECS 控制GpuEcsAnimatorAspect.RunAnimation()2. 烘焙阶段离线2.1 烘焙产出物产出Shader 属性 / 存储位置说明骨骼矩阵纹理_AnimatedBoneMatrices所有动画 × 所有帧 × 所有骨骼的 4×4 蒙皮矩阵骨骼权重Mesh UV1 / UV2 / UV3每顶点最多 6 根骨骼的(boneIndex, weight)动画元数据GpuEcsAnimatorBehaviour/ ECS Buffer每段动画的startFrameIndex、帧数、是否循环挂点数据可选GpuEcsAttachmentAnchorData挂点 Transform 每帧的 4×4 矩阵Material绑定矩阵纹理 Shader运行时不变关键代码入口GpuEcsAnimationBakerServices.GenerateAnimationObjectFromModel2.2 骨骼矩阵如何计算不是从骨骼上直接读取现成矩阵而是逐帧采样 Animator 后计算// GpuEcsAnimationBakeServices.cs - BakeAnimationMatricesTexturefor每一帧:Animator.Play(stateName,-1,progressRatio);Animator.Update(0);// 只更新骨骼 Transformfor每一根骨骼:matrixinv(网格世界矩阵)× bone.localToWorldMatrix × bindpose[boneIndex]写入纹理4像素/矩阵公式含义matrix inv(SkinnedMeshRenderer.transform) × boneTransform.localToWorldMatrix × bindpose与 Unity SkinnedMeshRenderer 标准 GPU 蒙皮矩阵一致将顶点从绑定姿态模型空间变换到当前动画姿态模型空间。2.3 采样参数采样帧率30 FPSGlobalConstants.SampleFrameRate每段动画帧数(int)(clip.length × 30) 1支持SingleClip和DualClipBlendBlend Tree 预采样2.4 烘焙时顶点是否参与不参与。逐帧循环只更新骨骼并计算矩阵顶点权重在独立步骤BakeBoneWeightsIntoMesh中只做一次静态写入 UV与动画时间无关。3. 骨骼矩阵纹理布局3.1 尺寸公式维度公式含义宽度骨骼数 × 4每根骨骼占 4 列像素4×4 矩阵的 4 列高度totalNbrOfFrames所有动画帧数累加格式RGBAFloat每像素 16 字节float4体积估算宽 × 高 × 16字节3.2 像素与矩阵1 像素 矩阵的 1 列4 个 float4 像素 完整 4×4 矩阵 64 字节存储方式按列写入float4(m[0][i], m[1][i], m[2][i], m[3][i])3.3 查表坐标Shader 中Load坐标// GpuEcsAnimator.cginc animatedBoneMatrices.Load(int3((boneIndex * 4) col, frameIndex, 0))XboneIndex × 4 colcol 0, 1, 2, 3YframeIndex3.4 布局示意[B0×4列] [B1×4列] [B2×4列] ... ← 宽 骨骼数 × 4 frame 0 │ M0 │ M1 │ M2 │ frame 1 │ M0 │ M1 │ M2 │ ... │ ... │ ... │ ... │ frame N │ M0 │ M1 │ M2 │ ← 高 totalNbrOfFrames4. CPU 运行时工作4.1 核心原则CPU不算蒙皮、不传骨骼矩阵只算查表参数。4.2 数据流游戏 SystemRunAnimation ↓ 写 animationID / speedFactor / blendFactor GpuEcsAnimatorSystemBurst Job ↓ 推进 normalizedTime算 frameIndex、帧间插值、多路混合 ↓ 写入 GpuEcsAnimatorShaderDataComponent.shaderData GpuEcsAnimatedMeshSystem ↓ 拷贝到 GpuEcsMaterialAnimationState.Value Entities Graphics [MaterialProperty] ↓ 渲染时自动上传 GPU4.3 每帧传给 GPU 的实例数据Shader 属性ECS 组件类型含义_AnimationStateGpuEcsMaterialAnimationStatefloat4x4查哪一行、插多少、怎么混_EnableAnimationGpuEcsMaterialEnableAnimationfloat0跳过蒙皮1启用_AnimationState每行最多 4 行分量含义[0]blendFactor该路动画混合权重[1]transitionToNextFrame当前帧 → 下一帧插值比例0~1[2]frameIndex纹理行索引整数帧[3]保留正常播放用 1~2 行动画切换过渡时 4 行全用旧动画 新动画 crossfade。4.4 游戏逻辑如何驱动// GpuEcsAnimatorAspectgpuEcsAnimatorAspect.RunAnimation((int)AnimationIdsEnemyGpu.Death);写入GpuEcsAnimatorControlComponentanimationID、speedFactor、blendFactor等。5. 数据如何传给 GPU5.1 不是什么方式本方案是否使用ComputeBuffer / StructuredBuffer否MaterialPropertyBlock否每帧 SetTexture 传矩阵否本项目血条、阴影等系统才用ComputeBufferGPUECSAnimationBaker不用。5.2 实际机制ECS MaterialProperty DOTS Instancing[MaterialProperty(_AnimationState)]publicstructGpuEcsMaterialAnimationState:IComponentData{publicfloat4x4Value;}业务代码只写 ECS 组件gpuEcsMaterialAnimationState.Value shaderDataUnityEntities Graphics在渲染时自动收集实例数据写入GPU Per-Instance BufferDOTS InstancingMaterial 需启用 Instancingm_EnableInstancingVariants: 15.3 两类数据的传法数据传法更新频率_AnimationState、_EnableAnimation[MaterialProperty]→ Instance Buffer每帧_AnimatedBoneMatrices纹理Material 绑定烘焙时SetTexture静态Mesh UV1/2/3 权重Mesh 顶点缓冲静态LocalTransform世界矩阵ECS 标准渲染每帧6. GPU 运行时工作全部在Vertex ShaderGpuEcsAnimator.cgincEnemyGpu.shadergraph子图完成。6.1 流程总览读 _AnimationState → 解析 frameIndex ↓ 读 UV1/2/3 → 解析 (boneIndex, weight) ↓ Load 纹理 → 拼 4×4 矩阵 ↓ 矩阵变换 加权累加LBS ↓ 帧间插值 → 多路动画混合 ↓ 输出 Position / Normal / Tangent ↓ Branch(_EnableAnimation) ? 蒙皮结果 : 原始顶点 ↓ LocalToWorld → MVP → 光照片元着色6.2 读矩阵并拼接float4 m0 loadBoneMatrixTexture(..., frameIndex, boneIndex, 0); float4 m1 loadBoneMatrixTexture(..., frameIndex, boneIndex, 1); float4 m2 loadBoneMatrixTexture(..., frameIndex, boneIndex, 2); float4 m3 loadBoneMatrixTexture(..., frameIndex, boneIndex, 3); float4x4 animatedBoneMatrix float4x4(m0, m1, m2, m3);6.3 单帧多骨骼加权对每个影响顶点骨骼最多 6 根positionOut boneWeight * mul(animatedBoneMatrix, float4(position, 1)).xyz; normalOut boneWeight * mul(rotationMatrix, normal); // 只用旋转部分 tangentOut boneWeight * mul(rotationMatrix, tangent);6.4 帧间插值pos (1 - t) * pos_frameN t * pos_frameN1t来自_AnimationState[i][1]frameIndex来自_AnimationState[i][2]。6.5 多路混合最多 4 路动画过渡、DualClipBlend每路乘blendFactor累加。6.6 蒙皮之后对象位移/旋转角色在场景中移动→ 常规 ECSLocalTransformLocalToWorld骨骼变形挥刀、跑步→ GPU 蒙皮模型空间两者分离先模型空间蒙皮再世界变换7. 加权蒙皮数学原理7.1 权重存储烘焙进 UVUV 通道内容UV1(bone0索引, weight0, bone1索引, weight1)UV2(bone2索引, weight2, bone3索引, weight3)UV3(bone4索引, weight4, bone5索引, weight5)超出maxNumberOfBonesPerVertex的骨骼会被截断并重新归一化保证 Σweight 1。7.2 Linear Blend SkinningLBS单帧公式p Σ (w_i × M_i × p) n Σ (w_i × R(M_i) × n)M_i完整 4×4 蒙皮矩阵含位移R(M_i)提取的纯旋转矩阵去掉缩放避免法线错误7.3 法线为何不用完整矩阵蒙皮矩阵含非均匀缩放时直接变换法线会出错。extractRotationMatrix对前三列归一化只保留旋转。8. 挂载道具剑/枪烘焙方案没有运行时骨骼 Transform 层级挂道具需额外处理。8.1 方案一绑骨骼蒙皮GPU推荐纯视觉道具原理剑/枪作为 SkinnedMesh顶点 100% 绑在手骨与身体共用 AnimatorGPU 蒙皮自动跟随。本项目 Player 的 Gun 即如此PlayerGpu_GpuEcsAnimator ├── Player ← GpuEcsAnimatedMeshBehaviour └── Gun ← GpuEcsAnimatedMeshBehaviour绑手骨优点缺点实现简单与身体完全一致不易做独立实体拾取、碰撞GPU 并行无额外 CPU需重新烘焙8.2 方案二Attachment Anchor 挂点CPU推荐可交互道具原理烘焙时额外记录手部挂点 Transform 每帧矩阵运行时 CPU 插值后写入剑实体的LocalTransformGPU当普通 Mesh 渲染不做 Shader 蒙皮。烘焙// 配置 AttachmentAnchor指向骨骼下 WeaponSocket 空节点anchorTransforms[frameIndex]anchorTransform.localToWorldMatrix;运行时GpuEcsAnimatorSystem → 插值挂点矩阵 → GpuEcsCurrentAttachmentAnchorComponent GpuEcsAttachmentSystem → 剑.LocalTransform 挂点矩阵 GPU → 常规 LocalToWorld × 顶点整物体变换非逐顶点蒙皮优点缺点剑是独立 ECS 实体可挂逻辑/碰撞额外 CPU SystemGPU 开销低标准 MVP需烘焙挂点数据注意方案二的 GPU不会在 Shader 里读挂点矩阵做逐顶点计算挂点矩阵在 CPU 侧写入实体 Transform。9. 项目实例Zombunny项值骨骼数35纹理尺寸140 × 383140 35×4总体积≈ 838 KB140×383×16 字节总帧数383动画分段AnimationIdsEnemyGpu动画startFrame帧数累计循环Move03838是Idle38301339否Death33944383否每帧矩阵数据35 × 64 字节 ≈2.2 KB/帧仅矩阵纹理部分。10. 关键点速查烘焙贴图烘焙的是蒙皮矩阵不是顶点位置矩阵 inv(网格矩阵) × 骨骼世界矩阵 × bindpose计算得来非直接提取逐帧采样只动骨骼顶点不参与宽 骨骼数×4高 总帧数每矩阵 4 像素RGBAFloatCPU 工作每帧只传_AnimationStatefloat4x4_EnableAnimation传的是 frameIndex、插值 t、混合权重不传骨骼矩阵通过[MaterialProperty] Entities Graphics不是 ComputeBufferGPU 工作Load 纹理 → 拼矩阵 → LBS 加权 → 帧间 lerp → 多路 blend法线用旋转矩阵位置用完整矩阵蒙皮在模型空间世界位移走LocalTransform挂道具绑骨骼GPU 蒙皮与身体同流程挂点CPU 写 TransformGPU 普通 Mesh 渲染优缺点优点缺点CPU 开销极低适合大量同屏内存随帧数线性增长DOTS Instancing 友好动画需离线烘焙不能运行时改实现相对简单LBS 动画混合可能有体积塌陷与 SkinnedMeshRenderer 对比SkinnedMeshRendererGPUECSAnimationBaker骨骼矩阵CPU 每帧计算烘焙进纹理CPU 每帧更新所有骨骼只传 ~64B 查表参数蒙皮位置CPU 或 GPU引擎内部GPU Vertex Shader 查纹理Animator运行时完整状态机烘焙固定动画集相关文件索引类型路径烘焙逻辑Assets/Thirds/GPUECSAnimationBaker/Engine/Baker/GpuEcsAnimationBakeServices.csCPU 动画 SystemAssets/Thirds/GPUECSAnimationBaker/Engine/AnimatorSystem/GpuEcsAnimatorSystem.csMaterial 同步 SystemAssets/Thirds/GPUECSAnimationBaker/Engine/AnimatorSystem/GpuEcsAnimatedMeshSystem.csMaterialProperty 组件Assets/Thirds/GPUECSAnimationBaker/Engine/AnimatorSystem/GpuEcsAnimatedMeshComponents.csGPU 蒙皮 HLSLAssets/Thirds/GPUECSAnimationBaker/Engine/Shader/GpuEcsAnimator.cginc挂点 SystemAssets/Thirds/GPUECSAnimationBaker/Engine/AnimatorSystem/GpuEcsAttachmentSystem.cs敌人 ShaderAssets/Res/Shader/EnemyGpu.shadergraph烘焙资源示例Assets/Res/Prefabs/EntityAnimG/BakedAssets_ZombunnyGpu/

相关新闻

深圳网站建设伪静态报价jsp语言:老站长掏心窝子的避坑指南与成本真相

深圳网站建设伪静态报价jsp语言:老站长掏心窝子的避坑指南与成本真相

最近后台收到不少朋友的私信,问的问题大同小异:“我想做个网站,听说伪静态对SEO好,但用JSP语言是不是很难搞?现在的报价到底有没有坑?”每当看到这些问题,我都忍不住想叹气。真的,现在的互联网圈子太浮躁了,很多人只盯着那个看起来光鲜亮丽的前端界面,却忽略了一开始…

2026/10/16 13:12:57 阅读更多 →
海外开发者神器盘点(6):文档与笔记类效率工具

海外开发者神器盘点(6):文档与笔记类效率工具

上一篇把终端配置做成可迁移的 dotfiles,本篇解决“命令会跑但没人知道为什么”的问题:用 Obsidian、Logseq 管个人知识,用 MkDocs、Docusaurus、VitePress 发布团队文档,用 Ditaxis 区分教程、操作指南、解释与参考。 一、痛点&…

2026/10/20 12:14:39 阅读更多 →
终极U盘启动盘制作指南:Rufus完整使用教程与深度解析

终极U盘启动盘制作指南:Rufus完整使用教程与深度解析

终极U盘启动盘制作指南:Rufus完整使用教程与深度解析 【免费下载链接】rufus The Reliable USB Formatting Utility 项目地址: https://gitcode.com/GitHub_Trending/ru/rufus 先交代一句:本文的主角是 Rufus——一款开源的、单文件即可运行的 U …

2026/10/20 13:09:46 阅读更多 →

最新新闻

NLTK情感分析实战:朴素贝叶斯文本分类与特征工程

NLTK情感分析实战:朴素贝叶斯文本分类与特征工程

开头我先交代一个背景。去年我接了一个某售后团队的文本处理需求,他们每个月有近万条用户反馈,需要把表达不满的留言单独捞出来,让客服优先跟进。一开始大家都以为这种“情绪识别”要上什么重磅模型,结果我用了NLTK这套老牌工具&a…

2026/10/20 15:11:55 阅读更多 →
H3C防火墙双机热备:RBM+VRRP原理与Web配置实战

H3C防火墙双机热备:RBM+VRRP原理与Web配置实战

做网络运维这些年,防火墙双机热备是我见过最务实、也最容易被忽视的一项配置。很多朋友一提到RBMVRRP,脑子里就浮现一堆命令行,要么对着文档一行行抄,要么在设备上死记硬背,最后改一个接口就全乱了。其实H3C防火墙这套…

2026/10/20 15:11:55 阅读更多 →
OWASP Benchmark实战:用黄金标准量化评估SAST/DAST工具能力

OWASP Benchmark实战:用黄金标准量化评估SAST/DAST工具能力

如果你负责过应用安全工具选型,大概率经历过这种尴尬:会议室里各家厂商把 SAST、DAST 工具演示得天花乱坠,漏洞检出率动辄宣称百分之九十几,可一旦回到真实业务,漏报和误报依旧扎心。这种局面本质上不是某个工具不行&a…

2026/10/20 15:11:55 阅读更多 →
VS2017编译64位libssh2:OpenSSL与Zlib三库协同实战指南

VS2017编译64位libssh2:OpenSSL与Zlib三库协同实战指南

简介:本资源是面向Windows平台C/C开发者的一站式libssh2 64位编译成果包,专为解决VS2017环境下64位应用程序集成SSH2协议能力的刚需。资源提供已成功编译的完整libssh2 Win64库及配套头文件、依赖DLL(含libcrypto-1_1-x64.dll与libssl-1_1-x6…

2026/10/20 15:11:55 阅读更多 →
路由器工作原理深度拆解:路由表、数据包转发与NAT

路由器工作原理深度拆解:路由表、数据包转发与NAT

上次帮邻居调网络,发现他家的光猫、路由器、交换机全都挤在弱电箱里,线乱成一团。他说自己只会“插上电源,连上Wi-Fi”,至于路由器里面到底干了什么,完全是个黑盒子。这其实是很普遍的现象——路由器天天在用&#xff…

2026/10/20 15:11:55 阅读更多 →
Apple Silicon 虚拟机 EFI_RNG_PROTOCOL 缺失排查

Apple Silicon 虚拟机 EFI_RNG_PROTOCOL 缺失排查

前两天我在一台 M2 的 Mac mini 上用 VMware Fusion 装 Ubuntu 22.04 ARM 版,系统安装完重启,屏幕刷过一长串启动日志,其中一行EFI_RNG_PROTOCOL unavailable特别扎眼。第一反应就是“启动失败了”。结果等了几秒,系统照常进到了登…

2026/10/20 15:10:51 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →