重新认识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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LODGroup的setLod方法是用来设置LOD(Level of Detail)的层级和渲染的重新构造的。通过设置LOD的层级和渲染器,可以实现在不同距离下显示不同的细节级别。在代码中,可以使用LODGroup的SetLODs方法来设置LOD的参数,然后使用RecalculateBounds方法重新计算边界。具体的代码示例如下: ```csharp LOD\[\] lods = new LOD\[2\]; Renderer\[\] renderers = new Renderer\[1\]; // 设置LOD0 renderers\[0\] = cubeTra.GetComponent<Renderer>(); lods\[0\] = new LOD(1.0F, renderers); lods\[0\].screenRelativeTransitionHeight = lodFloat; // 设置LOD1 lods\[1\] = new LOD(0f, renderers); lods\[1\].screenRelativeTransitionHeight = 0f; // 应用LOD cubeTra.parent.GetComponent<LODGroup>().SetLODs(lods); cubeTra.parent.GetComponent<LODGroup>().RecalculateBounds(); ``` 以上代码示例中,首先创建了一个包含两个LOD的数组lods,然后创建了一个包含一个渲染器的数组renderers。接着,设置了LOD0的参数,包括屏幕相对过渡高度和渲染器。然后,设置了LOD1的参数,同样包括屏幕相对过渡高度和渲染器。最后,使用LODGroup的SetLODs方法将LOD应用到LODGroup上,并使用RecalculateBounds方法重新计算边界。 #### 引用[.reference_title] - *1* *2* *3* [UnityLOD代码研究](https://blog.csdn.net/SieStar/article/details/122845461)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值