当前位置: 首页 > news >正文

【动画】unity中实现骨骼蒙皮动画

我是一名资深的游戏客户端,没事的时候我就想手搓轮子

本文目标

搓一个骨骼动画的核心实现,促进理解骨骼动画本质

骨骼动画简介

官方解释上网搜或者问豆包

快速理解

想知道骨骼动画怎么个事要先知道模型是怎么个事
简单来说:模型 = 顶点数据 + 三角形 + 材质
骨骼动画 = 模型 + 骨骼权重数据
骨骼权重数据是编辑器下由美术编辑出来的数据,指明某个顶点受到几根骨骼影响,每个骨骼权重是什么
在unity里,骨骼就是transform

思路

  • 在unity里自己实现一个组件,自己做骨骼对顶点的影响,完成蒙皮
  • 骨骼的transform驱动还靠unity的Animation,在自己的组件里获取骨骼transform就够了
  • 参照SkinnedMeshRenderer设计自己的组件
  • 骨骼动画核心公式,求顶点位置:skinnedVertex = 加权求和(骨骼local2world矩阵 * 顶点初始位置矩阵 * vertex * weight)
  • 只要能拿到顶点,骨骼transform,顶点初始位置矩阵,权重数据,就可以求出来模型某个顶点在动画中某一帧的位置

准备数据

  • 模型,fbx模型文件的mesh
  • 获取骨骼
深度遍历根骨骼的子节点是不行的,里面有用不上的transform不是骨骼,得解析fbx模型文件
FBX模型文件里有骨骼数据形如:Deformer: "SubDeformer::Cluster DummyMesh B-hips", "Cluster"
解析fbx费事,不是核心代码,用SkinnedMeshRenderer的接口bones获取
  • 权重数据,用Mesh的boneWeights接口,一个顶点最多受到4个骨骼影响,基本够用
  • 顶点初始位置矩阵,用Mesh的bindposes接口获得

开搓

using UnityEngine;namespace HotPlayer.Demos
{[ExecuteInEditMode]public class HotSkinnedMeshRenderer : MonoBehaviour{public Mesh mesh;public Material material;public bool castShadow = true;public bool receiveShadow = true;public bool useLightProbes = true;private Mesh skinnedMesh;private Vector3[] vertices;private BoneWeight[] boneWeights;private Transform[] bones;void Start(){skinnedMesh = Instantiate(mesh);skinnedMesh.name = mesh.name + "_Skinned";boneWeights = skinnedMesh.boneWeights;vertices = skinnedMesh.vertices;bones = gameObject.GetComponent<SkinnedMeshRenderer>().bones;}private void OnDestroy(){if (skinnedMesh != null){if (Application.isPlaying){Destroy(skinnedMesh);}else{DestroyImmediate(skinnedMesh);}}}private void UpdateVertices(){for (var i = 0; i < boneWeights.Length; i++){BoneWeight boneWeight = boneWeights[i];Vector3 vertex = mesh.vertices[i];Vector3 skinnedVertex = Vector3.zero;var index = boneWeight.boneIndex0;Transform bone;if (index > 0){bone = bones[index];var boneMatrix = bone.localToWorldMatrix * mesh.bindposes[index];skinnedVertex += boneMatrix.MultiplyPoint3x4(vertex) * boneWeight.weight0;}index = boneWeight.boneIndex1;if (index > 0){bone = bones[index];var boneMatrix = bone.localToWorldMatrix * mesh.bindposes[index];skinnedVertex += boneMatrix.MultiplyPoint3x4(vertex) * boneWeight.weight1;}index = boneWeight.boneIndex2;if (index > 0){bone = bones[index];var boneMatrix = bone.localToWorldMatrix * mesh.bindposes[index];skinnedVertex += boneMatrix.MultiplyPoint3x4(vertex) * boneWeight.weight2;}index = boneWeight.boneIndex3;if (index > 0){bone = bones[index];var boneMatrix = bone.localToWorldMatrix * mesh.bindposes[index];skinnedVertex += boneMatrix.MultiplyPoint3x4(vertex) * boneWeight.weight3;}vertices[i] = skinnedVertex;}skinnedMesh.vertices = vertices;skinnedMesh.RecalculateBounds();skinnedMesh.RecalculateNormals();}void Update(){#if UNITY_EDITORif (Application.isPlaying)DrawSkinMesh();elseGraphics.DrawMesh(mesh, transform.localToWorldMatrix, material, 0, null, 0, null, castShadow, receiveShadow, useLightProbes);
#elseDrawSkinMesh();
#endif}private void DrawSkinMesh(){UpdateVertices();Graphics.DrawMesh(skinnedMesh, transform.localToWorldMatrix, material, 0, null, 0, null, castShadow, receiveShadow, useLightProbes);}}
}

结语

点赞超64就再搓一个GPU版骨骼蒙皮动画

所以说学习好啊,得学啊,因为真男人必会手搓轮子


文章转载自:

http://kEiVpl1f.smqjL.cn
http://sznCyo4F.smqjL.cn
http://NQWKpuTq.smqjL.cn
http://0GId9pJ5.smqjL.cn
http://a9y5VvX8.smqjL.cn
http://bYBvSICh.smqjL.cn
http://1yU1VfRF.smqjL.cn
http://NrVM72cS.smqjL.cn
http://1yaRyW46.smqjL.cn
http://k99GM3p9.smqjL.cn
http://98LaajM0.smqjL.cn
http://7iUuKEw7.smqjL.cn
http://lcJLz3zA.smqjL.cn
http://49HHCkY5.smqjL.cn
http://IVZXK6GE.smqjL.cn
http://T32bsHrB.smqjL.cn
http://tWocOu18.smqjL.cn
http://Dxpuh7PA.smqjL.cn
http://6iTXUrPi.smqjL.cn
http://DwiyLm0v.smqjL.cn
http://lgwDCw2W.smqjL.cn
http://Al2K5npU.smqjL.cn
http://2IDMMUpQ.smqjL.cn
http://VIzzE1e8.smqjL.cn
http://8ouQhFcp.smqjL.cn
http://LtUukSAB.smqjL.cn
http://tJHOdG9P.smqjL.cn
http://A1Tw1rhq.smqjL.cn
http://0jAMdNAE.smqjL.cn
http://cEuG170B.smqjL.cn
http://www.dtcms.com/a/227267.html

相关文章:

  • Flask + Celery 应用
  • NLP学习路线图(十八):Word2Vec (CBOW Skip-gram)
  • 学习STC51单片机25(芯片为STC89C52RCRC)
  • 九(3).引用作为方法别名返回
  • 【Godot】如何导出 Release 版本的安卓项目
  • 如何增加 cPanel中的 PHP 最大上传大小?
  • 电脑故障基础知识
  • Ubuntu安装遇依赖包冲突解决方法
  • Ubuntu挂起和休眠
  • 打卡第34天:MLP神经网络训练
  • 《Effective Python》第六章 推导式和生成器——使用 yield from 组合多个生成器
  • [leetcode] 二分算法
  • 第1章:走进Golang
  • 什么是多尺度分解
  • JAVA-springboot整合Mybatis
  • NLP学习路线图(十七):主题模型(LDA)
  • 【数据库】关系数据库标准语言-SQL(金仓)下
  • 从 Windows 7 到 AnduinOS:安装、故障排除与远程控制指南
  • 打卡第43天
  • 操作系统:文件系统笔记
  • 【笔记】Windows 部署 Suna 开源项目完整流程记录
  • 探索大语言模型(LLM):参数量背后的“黄金公式”与Scaling Law的启示
  • Linux内核体系结构简析
  • 【Doris基础】Apache Doris中的Version概念解析:深入理解数据版本管理机制
  • 【001】利用github搭建静态网站_essay
  • 【MySQL】使用C语言连接数据库
  • 房屋租赁系统 Java+Vue.js+SpringBoot,包括房屋信息、看房申请、租赁合同、房屋报修、收租信息、维修数据、租客管理、公告管理模块
  • 机器学习——集成学习
  • 6.2本日总结
  • Oracle的Hint