赞
踩
参考了 Unity 论坛上的某个帖子:Level of Detail management (LOD)
虽然 Unity 没有内置的角色 LOD 管理
但是我们可以自己根据距离来切换网格
直接参考:https://pastebin.com/clone/K1S4Zjh0
using UnityEngine; using System; using System.Collections.Generic; //[ExecuteInEditMode] public class CharacterLOD : MonoBehaviour { public Mesh LOD0; public Mesh LOD1; public Mesh LOD2; public Mesh LOD3; public List<Material> LOD_0_Materials; public List<Material> LOD_1_Materials; public List<Material> LOD_2_Materials; public List<Material> LOD_3_Materials; public float lod0Dist = 10f; public float lod1Dist = 20f; public float lod2Dist = 30f; public float lod3Dist = 50f; public float lodTimer = 0.3f; public Camera camera1; private bool foundCamera; public int currLOD; private float currTimer; public void Start() { GetCamera(); currLOD = 0; } void Update() { if (foundCamera == false) GetCamera(); //Update LODs Timer currTimer -= Time.deltaTime; if (currTimer <= 0.0f) { UpdateMeshLOD(); currTimer = lodTimer; //Reset Timer } } public void UpdateMeshLOD() { if (camera1 == null) return; Vector3 camPos1 = camera1.transform.position; if ((transform.position - camPos1).sqrMagnitude < (lod0Dist * QualitySettings.lodBias) * (lod0Dist * QualitySettings.lodBias)) //LOD 0 { if (currLOD != 0) { SetLowestLODMesh(QualitySettings.maximumLODLevel); SetLODMaterials(QualitySettings.maximumLODLevel); currLOD = 0; return; } } else if ((transform.position - camPos1).sqrMagnitude < (lod1Dist * QualitySettings.lodBias) * (lod1Dist * QualitySettings.lodBias)) //LOD 1 { if (currLOD != 1) { SetLowestLODMesh(QualitySettings.maximumLODLevel + 1); SetLODMaterials(GetLowestLODMats(QualitySettings.maximumLODLevel + 1)); currLOD = 1; return; } } else if ((transform.position - camPos1).sqrMagnitude < (lod2Dist * QualitySettings.lodBias) * (lod2Dist * QualitySettings.lodBias)) //LOD 2 { if (currLOD != 2) { SetLowestLODMesh(QualitySettings.maximumLODLevel + 2); SetLODMaterials(GetLowestLODMats(QualitySettings.maximumLODLevel + 2)); currLOD = 2; return; } } else if ((transform.position - camPos1).sqrMagnitude < (lod3Dist * QualitySettings.lodBias) * (lod3Dist * QualitySettings.lodBias)) //LOD 3 { if (currLOD != 3) { SetLowestLODMesh(QualitySettings.maximumLODLevel + 3); SetLODMaterials(GetLowestLODMats(QualitySettings.maximumLODLevel + 3)); currLOD = 3; return; } } } public void SetLODMaterials(int lod) { Material[] currMats; bool wasSuccess = false; switch (lod) { case 0: //LOD 0 if (LOD_0_Materials.Count > 0) { int existingMatsCount = 0; currMats = new Material[LOD_0_Materials.Count]; for (var x = 0; x < LOD_0_Materials.Count; x++) { currMats[x] = LOD_0_Materials[x]; if (LOD_0_Materials[x] != null) existingMatsCount++; } if (existingMatsCount / LOD_0_Materials.Count > 0.5f) //Atleast 50% of materials exist { GetComponent<Renderer>().sharedMaterials = currMats; wasSuccess = true; } } break; case 1: if (LOD_1_Materials.Count > 0) { int existingMatsCount = 0; currMats = new Material[LOD_1_Materials.Count]; for (var x = 0; x < LOD_1_Materials.Count; x++) { currMats[x] = LOD_1_Materials[x]; if (LOD_1_Materials[x] != null) existingMatsCount++; } if (existingMatsCount / LOD_1_Materials.Count > 0.5f) //Atleast 50% of materials exist { GetComponent<Renderer>().sharedMaterials = currMats; wasSuccess = true; } } break; case 2: if (LOD_2_Materials.Count > 0) { int existingMatsCount = 0; currMats = new Material[LOD_2_Materials.Count]; for (var x = 0; x < LOD_2_Materials.Count; x++) { currMats[x] = LOD_2_Materials[x]; if (LOD_2_Materials[x] != null) existingMatsCount++; } if (existingMatsCount / LOD_2_Materials.Count > 0.5f) //Atleast 50% of materials exist { GetComponent<Renderer>().sharedMaterials = currMats; wasSuccess = true; } } break; case 3: if (LOD_3_Materials.Count > 0) { int existingMatsCount = 0; currMats = new Material[LOD_3_Materials.Count]; for (var x = 0; x < LOD_3_Materials.Count; x++) { currMats[x] = LOD_3_Materials[x]; if (LOD_3_Materials[x] != null) existingMatsCount++; } if (existingMatsCount / LOD_3_Materials.Count > 0.5f) //Atleast 50% of materials exist { GetComponent<Renderer>().sharedMaterials = currMats; wasSuccess = true; } } break; } if (wasSuccess) Debug.Log("[CharacterLOD] " + this.transform.root.name + " Swapped LOD Mats to: " + lod); } public int GetLowestLODMats(int desired) { if (desired >= 3) { if (LOD_3_Materials.Count > 0) return 3; if (LOD_2_Materials.Count > 0) return 2; if (LOD_1_Materials.Count > 0) return 1; if (LOD_0_Materials.Count > 0) return 0; } if (desired == 2) { if (LOD_2_Materials.Count > 0) return 2; if (LOD_1_Materials.Count > 0) return 1; if (LOD_0_Materials.Count > 0) return 0; } if (desired == 1) { if (LOD_1_Materials.Count > 0) return 1; if (LOD_0_Materials.Count > 0) return 0; } if (desired == 0) { if (LOD_0_Materials.Count > 0) return 0; } return 0; } public void SetLowestLODMesh(int desired) { SkinnedMeshRenderer mf1 = GetComponent<SkinnedMeshRenderer>(); if (desired >= 3) { if (LOD3 != null) mf1.sharedMesh = LOD3; if (LOD2 != null) mf1.sharedMesh = LOD2; if (LOD1 != null) mf1.sharedMesh = LOD1; if (LOD0 != null) mf1.sharedMesh = LOD0; } if (desired == 2) { if (LOD2 != null) mf1.sharedMesh = LOD2; if (LOD1 != null) mf1.sharedMesh = LOD1; if (LOD0 != null) mf1.sharedMesh = LOD0; } if (desired == 1) { if (LOD1 != null) mf1.sharedMesh = LOD1; if (LOD0 != null) mf1.sharedMesh = LOD0; } if (desired == 0) if (LOD0 != null) mf1.sharedMesh = LOD0; } public void GetCamera() { try { camera1 = Camera.main; foundCamera = true; } catch (Exception e) { Debug.Log("[CharacterLOD] Couldn't find Main Camera: " + e.Message); } } }
上面按距离的方式来处理的话会有一些问题
比如说再 FPS 游戏中就不太好用
因为游戏会有倍镜
而倍镜一般时调整 FOV 来处理的
再使用 LODGroup 中处理的时按屏幕高度比例来处理的话,这种制作时没有问题的
但时 LODGROUP 的方式不设置对 SkinnedMeshRenderer 做处理
因为 SkinnedMeshRenderer 内部有动画信息的状态
所以只能写脚本切换 Mesh 即可,动画骨骼信息不要动他就可以了
然后配上 自己按 FOV 的数值来换算一下,让 FOV 不同大小下也可以比较正常的显示 SknnedMeshRnederer 的 LOD
还有另一个问题,以上这些方式都不能切换骨骼结构
因为如果一个模型骨骼结构很复杂,也时消耗比较大的
但是理论上,我们可以再写一个:再切换 LOD时,可以将前一个LOD的 Animator 状态和 AnimationClip.playTime 同步一下到当前的 LOD 即可,但是不保证会不会抖动,得看 API 得封装够不够好
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。