Unity Occlusion Culling 遮挡剔除研究

本文章由cartzhang编写,转载请注明出处。 所有权利保留。 
文章链接:http://blog.csdn.net/cartzhang/article/details/52684127 
作者:cartzhang

一、unity裁剪包括,视锥裁剪和遮挡裁剪。

什么是视锥体裁剪? 
我们来直接看下官方的图解,看图说话。 
场景中的对象: 
https://docs.unity3d.com/uploads/Main/OcclusionNoCulling.png 
这里写图片描述 
视锥体的裁剪: 
https://docs.unity3d.com/uploads/Main/OcclusionFrustumCulling.png 
这里写图片描述 

那什么是遮挡裁剪呢? 
https://docs.unity3d.com/uploads/Main/OcclusionFullCulling.png 
这里写图片描述 
一图胜千言,这就是遮挡裁剪。 
要说的是,之前的此功能必须是pro版本才有的,现在使用的5.4.0f3版本是有的。 
遮挡裁剪的原理:通过在场景中使用一个虚拟的摄像机来创建一个物体潜在可视性状态(set)的层级. 这些数据可以让每个运行时间内的摄像机来确定什么能看见什么看不见。通过这些数据, Unity 将确定只把可以看见的物体送去渲染. 
使用二叉树进行处理,通过判断是否在相机中可以看到来,把看到的送去渲染。 
当然还可以通过划定一个盒子来确定裁剪空间,更多细节请自行参考官方。 
要说的是:当你使用遮挡剔除时你依然使用视锥体剔除,他们是并行不悖的。 

本来有同事,是关于怎么判断对象是否在相机内渲染。 
这个想来简单,找到对象的render,然后判断Renderer.isvisiable即可。 
这时候发现,他说这个没有卵用。那我就自己试试了。 
先写了个扩展方法:

public static class RendererExtensions
{
    public static bool IsVisibleFrom(this Renderer renderer, Camera camera)
    {
        Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);
        return GeometryUtility.TestPlanesAABB(planes, renderer.bounds);
    }
}


但是这个方法很明显只判断是否在视锥平头体内,不判断是否可见。 
那问题就在于遮挡裁剪啊,那看官方也琢磨了很久,一开始总是做不好。下面就是个记录历程,希望对看本文的你有所帮助。 
来吧,实践是检验真理的唯一标准啊。

二、首先搭建一个简单场景。

这里写图片描述
注意:要把所有的cube转换为静态的,static. 
其中有个小红色块是有代码的。 
脚本如下:


public class IsRenderByCamera : MonoBehaviour
{
    private Renderer rend;
    // Use this for initialization
    void Start ()
    {
        rend = this.GetComponent<Renderer>();
    }

    // Update is called once per frame
    void Update ()
    {
        //if (rend.IsVisibleFrom(Camera.main))
        //{
        //    Debug.Log("visible by main camera");
        //}
        //else
        //{
        //    Debug.Log("not visible by any camera");
        //}

        //if (rend.IsVisibleFrom(Camera.main)) Debug.Log("Visible");
        //else Debug.Log("Not visible");

        if (rend.isVisible)
        {
            Debug.LogError("red cube is Visible");
        }
        else
        {
            Debug.LogError("red cube not Visible"); ;
        }

        if (Camera.main.useOcclusionCulling)
        {
            Debug.Log("currrent occluson is using");
        }    
    }

    void OnWillRenderObject()
    {
        //Debug.Log("will render ");
    }


    void OnBecameVisible()
    {
        //Debug.Log("became Visible");
    }

    void OnBecameInvisible()
    {
        //Debug.Log("became InVisible");
    }


}


直接构建操作如下: 
这里写图片描述 
下面就不多说了,直接构建啊。 
构建完毕,看到如下结果: 
这里写图片描述
发现没有,坐标轴位置就是相机位置,注意有什么特点。 
在场景中直接拽动相机到大的遮挡版之前,发现没有发生任何所想的,所有对象依旧在渲染啊。 
这里写图片描述

三、这是怎么回事。

查找了各种资料和尝试,相机在大遮挡板之前,遮挡在其后的小块没有一个消失不渲染的。而相机进入到蓝色盒子里面的时候,就可以发现有小块依次的不渲染。 
这里写图片描述
这个就简单了、 
所以,在相机的后面添加了一个cube,记得也修改为静态的。构建后如下图: 
这里写图片描述

结果呢: 
这里写图片描述
真的可以,大功告成啊。

四、是不是全部都需要为静态的呢?

怎么办,继续测试实验啊。

实验一、

就把相机背后点,还有做边界的小块做为静态,其他都非静态。 
这里写图片描述

当然结果很明显了。没有遮挡版,无法实现遮挡裁剪。

实验二、

将大白板该为静态。 
这里写图片描述
可以实现对大白板后面对象的遮挡裁剪。 
这里写图片描述
而超越之后就没有效果了。 
这里写图片描述

五、注意:

1.若在unity编辑器中没有选中Occlusion,在场景中拖拽相机,并不会出现效果。这个很怀疑是bug. 
顺便说下,测试版本为5.4.0f3。 
2.还有就是必须在Visulize模型下才可以正常使用。 
这里写图片描述 
而在Edit模式下,不进行遮挡裁剪。 
这里写图片描述 
所以,怀疑Unity的版本只在编辑器下才有效果。写代码就是为打包测试、

六、打包测试

这里写图片描述

这里写图片描述

结果是打包后遮挡裁剪是正常的。可以使用。哈哈,嫌疑排除,自己想多了。 
但是怀疑精神还要有的。对吧? 
本来是判断对象是否在相机内渲染做处理的,误入歧途,做了这些工作,分享出来,自己做个记录,以飨读者。

七、工程地址:

Github:https://github.com/cartzhang/OcclusionCullingTest

参考:

https://docs.unity3d.com/Manual/OcclusionCulling.html 
http://bbs.9ria.com/thread-216913-1-1.html

八、—————–2017-03-09更新—–

**当前使用遮罩的时候,需要把它作为主场景,还必须有相机才可以。 

若用其他场景来加载此场景,则遮罩不起作用。**


Camera和 tris,verts的优化

Unity的Camera组件有很多可调节的参数,当需要做优化的时候,stats面板中的tris和verts这两个重点项都与Camera组件的参数有很大关系,有些参数的意义Unity手册说得不够详细,经过我们的测试和咨询,把结论记下来,如果有不对欢迎留言~

Field Of View 这个值的范围从1-179,是一个角度,具体是哪个角呢,是从Camera自身坐标系x轴方向看过去的红色夹角,如图所示,在这个角度下摄像机的远近裁剪面与y轴平行

Clipping Planes 远近裁剪面,就是下图中梯形的下底和上底

fov

接下来是本文的重点,请大家仔细阅读,我就一个常见问题,stats面板中显示的GPU渲染的面数tris及顶点数verts到底如何计算出来的?

这个问题之所以常见是因为影响Unity渲染效率或者游戏画面是否卡顿的最关键指标,就是stats面板中的tris和verts,搞清楚这两个指标的确切含义对于优化的意义不言而喻。

之前我一直以为这两个数据就是模型落在camera梯形中的顶点数,后来发现并不是这样,我把远裁剪面弄得与近裁剪面很接近,或者调整 field of view 都会改变这个梯形的体积,里面容纳的顶点看起来没有,但是stats面板里的数据确一直有。如下图,看起来这个梯形里没有顶点但是tris和verts的数据都显示GPU渲染出了那个矩形,12个tris(空场景本身有2个tris,剩下矩形6个4边面等于12个tris三角面)加24个verts(这个数字受多因素影响,不能从表现上解释):

fov_test_01

camera 组件参数如下(注意field of view)

fov_test_01_camera

当我改变 field of view 到 120 以上时候,在game视图下我们就能看到那个cube了,换句话说,真正的结论就是:

Unity stats 视图中的 tris 和 verts 并不仅仅是视锥中的梯形的 tris 和 verts,而是 field of view 所有取值下的 tris 和 verts,换句话说,哪怕你在当前game视图中看不到这个 cube,如果当你把 field of view 调大到 179 过程中都看不到这个 cube,stats面板才不会统计,gpu才不会渲染,否则,都会渲染(见下图),而且unity不会把模型拆分,这个模型哪怕只有1个顶点需要渲染,unity也会把整个模型都渲出来。

fov_test_02 fov_test_02_camera

 

Unity 模型优化中很常见的一个问题就是美术用max做出来的模型,在max里显示面数和顶点数少于unity中的,或者出现不一致的情况,根据我们的实际项目经历原因:

1)是max的数据显示的是四边面,而unity里status面板里的面tris是三角面,这两个的对应关系我根据数据总结是三角面的数量是四边面的2倍。

2)大家看一下这篇博主的文章 Unity 与 max 中顶点数面数区别的原因,其中提到的  Unity 这种将顶点一分为多的原因,主要有两个:一个是UV splits,一个是Smoothing splits。我们遇到的主要是 UV split,用通俗的语言解释就是UV 分割(max中用了几张UV网),比如一个人,他的UV分为头,身子两部分,一个车UV分为窗户,轱辘等等,UV分的越多,到Unity中的顶点数就会越多。所以最终的结果就是有的模型面多顶点少(如人物),有的模型面少顶点多(结构复杂的车),这也就解释了为什么max和Unity中模型的面数一般是一致的但是顶点数并没有直接的对应关系。

3)不同的制作方式对模型最终到Unity里也就是GPU的渲染下也会有不一样的结果,下面上一组图,三个不同颜色的加号,因为制作方式的不同导致了截然不同的渲染效率(而且这点往往和美术同学的认识相悖)

以下三个加号,绿的是两个box横竖穿插,红的是一个矩形挤压出横向矩形,黄的是一个竖box加左右两个横向box删掉横向box与纵向重叠的面,具体表现大家看数据吧~

green red yellow


猜你喜欢

转载自blog.csdn.net/kenkao/article/details/80950890