重新认识LODGroup

之前用Unity的LOD Group一直都是简单用了一下,今天才发现有好几个认知都是错误的,需要重新认识下LODGroup

LODGroup百分比并不是简单的距离百分比

之前以为LOD就是根据距离的远近然后切换的,LODGroup上面的百分比是距离的百分比,因此也一直很疑惑,这个距离的基准是多少,在哪里设置也没有找到。
在这里插入图片描述
而事实上,这个百分比是边界(Bounds)占屏幕的大小比,与距离有一定关系,但距离只是其中一个计算因子。
其实,我们仔细想想,就会发现如果用距离作为标准的话,每一个物体几乎都得设置自己的LOD切换分界值,因为对于巨大的物体,即使距离远了,但可能在屏幕上的大小依然很大,对于较小的物体,即使距离近了,在屏幕上也是很小,这样子的话设置LOD就会比较复杂。而使用占屏幕的大小比,就可以得到一个大体一致的标准。
从Gizmos也可以看得出来
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
从代码上
虽然LODGroup的代码基本看不了,都是内部方法,但它的Editor代码还是有一些信息的
根据我们设置的百分比,去倒推所需要的摄象机距离,然后再根据相机的正交还是透视的设置,去设置场景相机的设置

LODGroupEditor.cs

       // Set the camera distance so that the current LOD group covers the desired percentage of the screen
        private static void UpdateCamera(float desiredPercentage, LODGroup group)
        {
    
    
            var worldReferencePoint = LODUtility.CalculateWorldReferencePoint(group);
            var percentage = Mathf.Max(desiredPercentage / QualitySettings.lodBias, 0.000001f);
            var sceneView = SceneView.lastActiveSceneView;
            var sceneCamera = sceneView.camera;
            // Figure out a distance based on the percentage
            var distance = LODUtility.CalculateDistance(sceneCamera, percentage, group);
            // We need to do inverse of SceneView.cameraDistance:
            // given the distance, need to figure out "size" to focus the scene view on.
            float size;
            if (sceneCamera.orthographic)
            {
    
    
                size = distance;
                if (sceneCamera.aspect < 1.0)
                    size *= sceneCamera.aspect;
            }
            else
            {
    
    
                var fov = sceneCamera.fieldOfView;
                size = distance * Mathf.Sin(fov * 0.5f * Mathf.Deg2Rad);
            }
            SceneView.lastActiveSceneView.LookAtDirect(worldReferencePoint, sceneCamera.transform.rotation, size);
        }

倒推距离的方法,我们从参数名relativeScreenHeight就可以看出来,用的是相对屏幕的高度,就是边界高/屏幕高
LODUtility.bindings.cs

  [FreeFunction("LODUtilityBindings::CalculateDistance")]
        extern internal static float CalculateDistance(Camera camera, float relativeScreenHeight, LODGroup group);

LOD Bias的真正意义

在这里插入图片描述

之前看资料时,大部分的文档都是和官方文档差不多的描述,意思是那么个意思,但总感觉不是很清楚
在这里插入图片描述
看了下面源码中的这一句,其实就很好理解了,其实LOD Bias就是一个缩放倍数,

var percentage = Mathf.Max(desiredPercentage / QualitySettings.lodBias, 0.000001f);

比如原来我们设定的100%-50%显示LOD0,如果我们设置LOD Bias为2,那就是实际边界高占屏幕比时50%-25%时显示LOD0,但由于向上没有更高的LOD,所以最后是100%-25%显示LOD0,
在这里插入图片描述
或者换个思路理解,LOD Bias为1时,基准100%就是100%
而LOD Bias为2时,基准100%,实际对应的比值只有50%了,后面所有的比值界线都得除以2
在这里插入图片描述

Maximum LOD Level 有点鸡肋

在这里插入图片描述
看Maximum LOD Level的文档描述还以为这是个高级功能,能够快速的实现内存的优化,但实际挺鸡肋的
根据测试结果:
对AssetBundle无优化:不会减少AssetBundle的加载,所有级别的AB都会关联加载
对Mesh的加载优化很小:只要有设置0,所有级别的Mesh都会被加载,只有将最高级质量级别设置为1,那么0级的Mesh才不会被加载,以此类推
所以这个功能有点鸡肋,原来还想用在对移动端的设备分级,但这样就一点效果都没有
或许就像文档里描述的,对不同平台有些作用,比如PC平台有LOD0-LOD2,而移动端平台只有LOD1-LOD2,这样可以启到一小点优化作用
不知道是不是我们没有发现真正的用法,但目前测试的事实如此,有大佬路过还请指导一下
目前后续是准备让美术场景中用Unity的LODGroup,然后导出时再换成自己的LOD组件

本文

https://blog.csdn.net/ithot/article/details/121598957?spm=1001.2014.3001.5502

一些有价值的参考代码

private static int GetLODCurShowLevel(Camera cam, LODGroup lodGroup)
{
    
    
    //var inv_SceneViewCamHeight = 1.0f / (cam.pixelHeight - 6.0f);
    var inv_SceneViewCamHeight = 1.0f / (cam.pixelHeight);

    var lods = lodGroup.GetLODs();
    for (int lodIDX = 0; lodIDX < lods.Length; lodIDX++)
    {
    
    
        var lod = lods[lodIDX];
        var renderers = lod.renderers;
        for (int renderIDX = 0; renderIDX < renderers.Length; renderIDX++)
        {
    
    
            var renderer = renderers[renderIDX];

            // method1:
            //var heightInScreen = Mathf.Abs(cam.WorldToScreenPoint(renderer.bounds.max).y - cam.WorldToScreenPoint(renderer.bounds.min).y);

            // method2:
            var heightInScreen = GetHeightInScreen(cam, renderer);
            var ratioInScren = heightInScreen * inv_SceneViewCamHeight;
            if (ratioInScren > lod.screenRelativeTransitionHeight)
            {
    
    
                return lodIDX;
            }
        }
    }

    return -1;
}

private static float GetHeightInScreen(Camera cam, Renderer renderer)
{
    
    
    var min = renderer.bounds.min;
    var max = renderer.bounds.max;
    // F = Front
    var FTL = new Vector3(min.x, max.y, min.z);
    var FTR = new Vector3(max.x, max.y, min.z);
    var FBR = new Vector3(max.x, min.y, min.z);
    var FBL = new Vector3(min.x, min.y, min.z);

    // Back
    var BTL = new Vector3(min.x, max.y, max.z);
    var BTR = new Vector3(max.x, max.y, max.z);
    var BBR = new Vector3(max.x, min.y, max.z);
    var BBL = new Vector3(min.x, min.y, max.z);

    // to screen space pos
    FTL = cam.WorldToScreenPoint(FTL);
    FTR = cam.WorldToScreenPoint(FTR);
    FBR = cam.WorldToScreenPoint(FBR);
    FBL = cam.WorldToScreenPoint(FBL);

    BTL = cam.WorldToScreenPoint(BTL);
    BTR = cam.WorldToScreenPoint(BTR);
    BBR = cam.WorldToScreenPoint(BBR);
    BBL = cam.WorldToScreenPoint(BBL);

    var maxY = FTL.y;
    maxY = Mathf.Max(FTR.y, maxY);
    maxY = Mathf.Max(FBR.y, maxY);
    maxY = Mathf.Max(FBL.y, maxY);

    maxY = Mathf.Max(BTL.y, maxY);
    maxY = Mathf.Max(BTR.y, maxY);
    maxY = Mathf.Max(BBR.y, maxY);
    maxY = Mathf.Max(BBL.y, maxY);

    var minY = FTL.y;
    minY = Mathf.Min(FTR.y, minY);
    minY = Mathf.Min(FBR.y, minY);
    minY = Mathf.Min(FBL.y, minY);

    minY = Mathf.Min(BTL.y, minY);
    minY = Mathf.Min(BTR.y, minY);
    minY = Mathf.Min(BBR.y, minY);
    minY = Mathf.Min(BBL.y, minY);

    return maxY - minY;
}

参考

http://blog.coolcoding.cn/?p=709
https://stackoverflow.com/questions/37755510/current-lod-level-lod-group-unity
https://github.com/JulienHeijmans/EditorScripts/blob/8482fea14852ce8032ed50060ab18557ce15f984/Scripts/Utility/Editor/LODExtendedUtility.cs#L84

猜你喜欢

转载自blog.csdn.net/ithot/article/details/121598957