当前位置:   article > 正文

Unity - 人物对象的 LOD 管理

Unity - 人物对象的 LOD 管理


Unity 没有内置的人物角色 LOD 管理

参考了 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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268

扩展、及其问题

上面按距离的方式来处理的话会有一些问题

比如说再 FPS 游戏中就不太好用

因为游戏会有倍镜

而倍镜一般时调整 FOV 来处理的

再使用 LODGroup 中处理的时按屏幕高度比例来处理的话,这种制作时没有问题的

但时 LODGROUP 的方式不设置对 SkinnedMeshRenderer 做处理

因为 SkinnedMeshRenderer 内部有动画信息的状态

所以只能写脚本切换 Mesh 即可,动画骨骼信息不要动他就可以了

然后配上 自己按 FOV 的数值来换算一下,让 FOV 不同大小下也可以比较正常的显示 SknnedMeshRnederer 的 LOD


还有另一个问题,以上这些方式都不能切换骨骼结构

因为如果一个模型骨骼结构很复杂,也时消耗比较大的

但是理论上,我们可以再写一个:再切换 LOD时,可以将前一个LOD的 Animator 状态和 AnimationClip.playTime 同步一下到当前的 LOD 即可,但是不保证会不会抖动,得看 API 得封装够不够好


另一种更好的方式

  • 首先得有 带骨骼蒙皮的 模型,和 或对应的 LOD1, LOD2
  • 直接给父级添加 LODGroup ,设置好每一级的 LOD renderers 就OK了
  • 可以无缝LOD切换

References

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/思考机器5/article/detail/60250
推荐阅读
相关标签
  

闽ICP备14008679号