unity模型导入
1.导入包来自游戏蛮牛,直接是unitypackage,但是路径不喜欢,自己换模型路径(贴图,材质球,动作),统一缩放值
下面聊一下Mesh、MeshFiler、MeshRenderer、skinMeshRender
mesh Filter是从资源里获取mesh资源,通过meshrender渲染到屏幕
Mesh:村抽三维模型数据(vertices顶点V3[]、triangles 三角形顶点索引 int[]、法线向量数组V3[],uv纹理坐标V2[])
MeshFiler:用于增加mesh属性(可以代码动态生产)
MeshRender:增加材质并渲染出来。
下面试创建一个Trianglegameobject
public GameObject GetTriangle () { GameObject go = new GameObject ("Triangle"); MeshFilter filter = go.AddComponent<MeshFilter> (); // 构建三角形的三个顶点,并赋值给Mesh.vertices Mesh mesh = new Mesh (); filter.sharedMesh = mesh; mesh.vertices = new Vector3[] { new Vector3 (0, 0, 1), new Vector3 (0, 2, 0), new Vector3 (2, 0, 5), }; // 构建三角形的顶点顺序,因为这里只有一个三角形, // 所以只能是(0, 1, 2)这个顺序。 mesh.triangles = new int[3] {0, 1, 2}; mesh.RecalculateNormals (); mesh.RecalculateBounds (); // 使用Shader构建一个材质,并设置材质的颜色。 Material material = new Material (Shader.Find ("Diffuse")); material.SetColor ("_Color", Color.yellow); // 构建一个MeshRender并把上面创建的材质赋值给它, // 然后使其把上面构造的Mesh渲染到屏幕上。 MeshRenderer renderer = go.AddComponent<MeshRenderer> (); renderer.sharedMaterial = material; return go; }
基本原理可概括为:在骨骼控制下,通过顶点混合动态计算蒙皮网格的顶点,而骨骼的运动相对于其父骨骼,并由动画关键帧数据驱动。一个骨骼动画通常包括骨骼层次结构数据,网格(Mesh)数据,网格蒙皮数据(skin info)和骨骼的动画(关键帧)数据
bip001 以及它之下骨骼数53个
步骤:
1.替换蒙皮网格(或者直接替换模型换装部位的GameObject,因为合并的时候会合并所有的蒙皮网格,而不会关心它是否属于原来角色身体的一部分,而且如果需要替换的部位有多个配件拥有独立的网格和贴图,那这种方式都可以正常执行。)
2.合并所有蒙皮网格
3.刷新骨骼
4.附加材质(我下面是获取第一个材质作为默认材质)
5.合并贴图(贴图的宽高最好是2的N次方的值)
6.重新计算UV
using System; using System.Collections; using UnityEngine; using System.Collections.Generic; public class CharacterCombine : MonoBehaviour { // 目标物体(必须是骨骼的父物体,不然蒙皮失效) public GameObject target; // 最终材质(合并所有模型后使用的材质) public Material material; // 物体所有的部分 private GameObject[] targetParts = new GameObject[9]; private string[] defaultEquipPartPaths = new string[9]; void Start() { // 把FBX的模型按部件分别放入Resources下对应的文件夹里,可以留空,模型需要蒙皮,而且所有模型使用同一骨骼 // 最后的M是Fbx的模型,需要的Unity3D里设置好材质和贴图,部件贴图要勾选Read/Write Enabled defaultEquipPartPaths[0] = "Model/Player/GirlPlayer/Head/Head0000/M"; defaultEquipPartPaths[1] = "Model/Player/GirlPlayer/Face/Face0000/M"; defaultEquipPartPaths[2] = "Model/Player/GirlPlayer/Hair/Hair0000/M"; defaultEquipPartPaths[3] = ""; defaultEquipPartPaths[4] = "Model/Player/GirlPlayer/Body/Body0000/M"; defaultEquipPartPaths[5] = "Model/Player/GirlPlayer/Leg/Leg0000/M"; defaultEquipPartPaths[6] = "Model/Player/GirlPlayer/Hand/Hand0000/M"; defaultEquipPartPaths[7] = "Model/Player/GirlPlayer/Foot/Foot0000/M"; defaultEquipPartPaths[8] = "Model/Player/GirlPlayer/Wing/Wing0001/M"; Destroy(target.GetComponent<SkinnedMeshRenderer>()); for (int i = 0; i < defaultEquipPartPaths.Length; i++) { UnityEngine.Object o = Resources.Load(defaultEquipPartPaths[i]); if (o) { GameObject go = Instantiate(o) as GameObject; go.transform.parent = target.transform; go.transform.localPosition = new Vector3(0, -1000, 0); go.transform.localRotation = new Quaternion(); targetParts[i] = go; } } StartCoroutine(DoCombine()); } /// <summary> /// 使用延时,不然某些GameObject还没有创建 /// </summary> /// <returns></returns> IEnumerator DoCombine() { yield return null; Combine(target.transform); } /// <summary> /// 合并蒙皮网格,刷新骨骼 /// 注意:合并后的网格会使用同一个Material /// </summary> /// <param name="root">角色根物体</param> private void Combine(Transform root) { float startTime = Time.realtimeSinceStartup; List<CombineInstance> combineInstances = new List<CombineInstance>(); List<Transform> boneList = new List<Transform>(); Transform[] transforms = root.GetComponentsInChildren<Transform>(); List<Texture2D> textures = new List<Texture2D>(); int width = 0; int height = 0; int uvCount = 0; List<Vector2[]> uvList = new List<Vector2[]>(); // 遍历所有蒙皮网格渲染器,以计算出所有需要合并的网格、UV、骨骼的信息 foreach (SkinnedMeshRenderer smr in root.GetComponentsInChildren<SkinnedMeshRenderer>()) { for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++) { CombineInstance ci = new CombineInstance(); ci.mesh = smr.sharedMesh; ci.subMeshIndex = sub; combineInstances.Add(ci); } uvList.Add(smr.sharedMesh.uv); uvCount += smr.sharedMesh.uv.Length; if (smr.material.mainTexture != null) { textures.Add(smr.GetComponent<Renderer>().material.mainTexture as Texture2D); width += smr.GetComponent<Renderer>().material.mainTexture.width; height += smr.GetComponent<Renderer>().material.mainTexture.height; } foreach (Transform bone in smr.bones) { foreach (Transform item in transforms) { if (item.name != bone.name) continue; boneList.Add(item); break; } } } // 获取并配置角色所有的SkinnedMeshRenderer SkinnedMeshRenderer tempRenderer = root.gameObject.GetComponent<SkinnedMeshRenderer>(); if (!tempRenderer) { tempRenderer = root.gameObject.AddComponent<SkinnedMeshRenderer>(); } tempRenderer.sharedMesh = new Mesh(); // 合并网格,刷新骨骼,附加材质 tempRenderer.sharedMesh.CombineMeshes(combineInstances.ToArray(), true, false); tempRenderer.bones = boneList.ToArray(); tempRenderer.material = material; Texture2D skinnedMeshAtlas = new Texture2D(get2Pow(width), get2Pow(height)); Rect[] packingResult = skinnedMeshAtlas.PackTextures(textures.ToArray(), 0); Vector2[] atlasUVs = new Vector2[uvCount]; // 因为将贴图都整合到了一张图片上,所以需要重新计算UV int j = 0; for (int i = 0; i < uvList.Count; i++) { foreach (Vector2 uv in uvList[i]) { atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x); atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y); j++; } } // 设置贴图和UV tempRenderer.material.mainTexture = skinnedMeshAtlas; tempRenderer.sharedMesh.uv = atlasUVs; // 销毁所有部件 foreach (GameObject goTemp in targetParts) { if (goTemp) { Destroy(goTemp); } } Debug.Log("合并耗时 : " + (Time.realtimeSinceStartup - startTime) * 1000 + " ms"); } /// <summary> /// 获取最接近输入值的2的N次方的数,最大不会超过1024,例如输入320会得到512 /// </summary> private int get2Pow(int into) { int outo = 1; for (int i = 0; i < 10; i++) { outo *= 2; if (outo > into) { break; } } return outo; } }