Unity5 SkinnedMesh 换装
发表于2018-10-15
这篇文章主要给大家介绍下Unity5中的 SkinnedMesh换装。
游戏中常见的换装做法 :
- 直接更换贴图
- 更换静态Mesh
- 更换SkinnedMesh
一、SkinnedMesh原理
SkinnedMesh中文一般称作骨骼蒙皮动画,正如其名,这种动画中包含骨骼(Bone)和蒙皮(Skinned Mesh)两个部分,Bone的层次结构和关节动画类似,Mesh则和关节动画不同:关节动画中是使用多个分散的Mesh,而Skinned Mesh中Mesh是一个整体,也就是说只有一个Mesh,实际上如果没有骨骼让Mesh运动变形,Mesh就和静态模型一样了。
Skinned Mesh技术的精华在于蒙皮,所谓的皮并不是模型的贴图(也许会有人这么想过吧),而是Mesh本身,蒙皮是指将Mesh中的顶点附着(绑定)在骨骼之上,而且每个顶点可以被多个骨骼所控制,这样在关节处的顶点由于同时受到父子骨骼的拉扯而改变位置就消除了裂缝。
Skinned Mesh这个词从字面上理解似乎是有皮的模型,哦,如果贴图是皮,那么普通静态模型不也都有吗?所以我觉得应该理解为具有蒙皮信息的Mesh或可当做皮肤用的Mesh,这个皮肤就是Mesh。而为了有皮肤功能,Mesh还需要蒙皮信息,即Skin数据,没有Skin数据就是一个普通的静态Mesh了。Skin数据决定顶点如何绑定到骨骼上。顶点的Skin数据包括顶点受哪些骨骼影响以及这些骨骼影响该顶点时的权重(weight),另外对于每块骨骼还需要骨骼偏移矩阵(BoneOffsetMatrix)用来将顶点从Mesh空间变换到骨骼空间。
在本文中,提到骨骼动画中的Mesh特指这个皮肤Mesh,提到模型是指骨骼动画模型整体。骨骼控制蒙皮运动,而骨骼本身的运动呢?当然是动画数据了。每个关键帧中包含时间和骨骼运动信息,运动信息可以用一个矩阵直接表示骨骼新的变换,也可用四元数表示骨骼的旋转,也可以随便自己定义什么只要能让骨骼动就行。除了使用编辑设定好的动画帧数据,也可以使用物理计算对骨骼进行实时控制。
二、美术制作
1、为了方便说明:把美术给的fbx放到unity中说明,看如下图。
2、这里是已经绑定好的骨骼,每个部分都包含对应的骨骼信息。这个都是美术来做的。换装也就简单了,比如上图让美术再做一个face-2、face-3。以后程序中就可以轻易替换脸了。当然也有美术是这么做的另给一个fbx包含信息跟上面一样,骨骼是相同的,拆分的部分是不相同,但对应的骨骼也是绑定好的。
三、导出AssetBundle资源
1、这里面我说下导出规则。首先要完整的导出骨骼如上图Bip001,其次将拆部分带出,并且顺带导出部分的bone骨骼信息。导出骨骼信息有什么用,接下来整合部分我会说明。完全可以自己新建一个prefba把他们都拷贝出来,然后导出prefab。这里要说明下有些美术绑定骨骼时候,要用到跟节点,骨骼名字是不能改变的,所以如上再创建prefab需要包含完整结构。如下图,包含Bip001
2、具体如何导出AssetBundle这里不做说明。
四、合并骨骼
1、先加载拆分部分AssetBundle资源获取bone信息(其实就是完整骨骼中Transform的名字集合)并且获取SkinnedMeshRenderer组件。
2、脚本组合
创建类信息为拆分组件获取的信息。保存在list中
privateList<Avatar>listAvatar=newList<Avatar>(); classAvatar{ publicSkinnedMeshRendererskin; publicstring[]data; } //第一个参数是完整骨骼创建出GameObject。第二参数是上面所有拆分部件得到的信息 privateGameObjectCreatPerson(GameObjectroot,List<Avatar>ava){ List<CombineInstance>combineInstances=newList<CombineInstance>(); List<Material>materials=newList<Material>(); List<Transform>bones=newList<Transform>(); Transform[]transforms=root.GetComponentsInChildren<Transform>(); foreach(Avatarelementinava){ SkinnedMeshRenderersmr=element.skin; materials.AddRange(smr.sharedMaterials); for(intsub=0;sub<smr.sharedMesh.subMeshCount;sub++){ CombineInstanceci=newCombineInstance(); ci.mesh=smr.sharedMesh; ci.subMeshIndex=sub; combineInstances.Add(ci); } intstartBoneIndex=bones.Count; foreach(stringboneinelement.data){ foreach(Transformtransformintransforms){ if(transform.name!=bone) continue; bones.Add(transform); break; } } intendBoneIndex=bones.Count; for(inti=1;i<smr.sharedMesh.subMeshCount;++i){//多个材质时需要再添加一次骨骼 for(intj=startBoneIndex;j<endBoneIndex;++j){ bones.Add(bones[j]); } } } SkinnedMeshRendererr=root.AddComponent<SkinnedMeshRenderer>(); r.sharedMesh=newMesh(); r.sharedMesh.CombineMeshes(combineInstances.ToArray(),false,false); r.bones=bones.ToArray(); r.materials=materials.ToArray(); returnroot; }
来自:https://blog.csdn.net/st75033562/article/details/52291092