RT Occlusion Culling

Hi, everyone. It is a dynamic-occlusion-culling system for Unity.

ASSETSTORE : RT Occlusion Culling : Dynamic Occlusion Culling | Camera | Unity Asset Store

  • Very easy to set up, no programming required.
  • Easy interface to edit convex hull
  • [1.2] Stereoscopic rendering support (VR)
  • [1.3] Baking LOD Mesh (Integrated UnityMeshSimplifier )
  • Geometric approach (NO GPU, NO Rasterization)
  • Available for any platform, including iOS and Android
  • Managing large-scale occludees by spatial partitioning algorithm
  • Runtime combine mesh for batching optimization

Compare with PVS (Unity Occlusion Culling)

  • Smaller memory usage (ignore, almost zero)
  • No pre-computing time, no pre-computing data
  • Dynamic Occluder (any translation, rotation, scaling)
  • Dynamic Environment (show and hide)
  • Cons. Setting occluders manually like unity-colliders for collision detection

Hi there, I’m looking for a decent occlusion culling tool. It looks promising, though I have to honest I’m not a fan of having to set all the occludees up like that. However if it gives a proper performance boost it’s probably worth the hassle.

But, I notice you barely mention the performance gains anywhere. I know this is very situation dependent, but could you at least show a video with a bit of a complex scene where you compare the scene with and without your tool?

1 Like

We made a comparison video with an old test device (NVidia shield tablet 2015) for you.
(We played the scene included in the asset.)

does it works with terrains ? webgl ?

@GCatz_1 webgl yes, unity terrains no…you can hide just whole terrain game object, but not its submeshes

@KSI777
I am trying to use custom object as convex occluder using script but as you can see in video I am getting weird looking yellow lines from camera…it seems that just feed “SetConvexVolume” function with Vector3 array of vertices from mesh filter component is not enough. What is the correct workflow? Thanks for help.
video: Zippyshare.com - [now defunct] Free File Hosting

…I am using mesh based terrain objects

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetOccluderConvex : MonoBehaviour
{
    RT.Occluder m_Occluder;
  
    void Awake()
    {
        m_Occluder = new RT.Occluder();
        m_Occluder.SetConvexVolume(GetComponent<MeshFilter>().mesh.vertices, transform.localToWorldMatrix);
        m_Occluder.SetTransform(transform);
    }
    void OnEnable()
    {
        if (m_Occluder != null)
            m_Occluder.Enable();
    }
    private void OnDisable()
    {
        if (m_Occluder != null)
           m_Occluder.Disable();
    }
}

@GCatz_1
WebGL has no problem because it works with the CPU.
But there is no support for terrain rendering.
It is impossible to apply the terrain as a occludee.
I think it might be able to use it with terrain partially.
I have captured a sample video for you. (Of course I think that is quite limited.)

@Bzuco
We checked and found some issues in SetConvexVolume.
(The limit for the number of planes is 64, and there were some errors in the calculation of the convex.)
We have fixed it and upload it to the asset store now, and it will be updated soon. (1.4 version)
If the update is delayed, request by email, and I will send changes to you.

The vertex count of convex affects performance.
So we added a function to limit the vertex to the convex calculation function.
And it would be helpful for the performance to pre-calculate it.

We added it to your code as below.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class SetConvexOccluder : MonoBehaviour
{
    RT.Occluder m_Occluder;
    [HideInInspector] public Vector3[] m_BakedConvex;
    [HideInInspector] public int[] m_BakedHullTriangle;
    [HideInInspector] public int[] m_BakedEdge;

    void Awake()
    {
        m_Occluder = new RT.Occluder();
        if (m_BakedConvex != null && m_BakedConvex.Length >= 4)
            m_Occluder.SetVolume(m_BakedConvex, m_BakedHullTriangle, m_BakedEdge, transform.localToWorldMatrix);
        else
            m_Occluder.SetConvexVolume(GetComponent<MeshFilter>().mesh.vertices, transform.localToWorldMatrix);
        m_Occluder.SetTransform(transform);
    }
    void OnEnable()
    {
        if (m_Occluder != null)
            m_Occluder.Enable();
    }
    private void OnDisable()
    {
        if (m_Occluder != null)
            m_Occluder.Disable();
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(SetConvexOccluder))]
    public class SetConvexOccluderEditor : Editor
    {
        Vector3[] m_ConvexVertex;
        int[] m_ConvexPolygons;
        int m_TargetCount;

        public void OnEnable()
        {
            SetConvexOccluder _target = (SetConvexOccluder)target;
            m_TargetCount = _target.m_BakedConvex != null && _target.m_BakedConvex.Length > 0 ? _target.m_BakedConvex.Length : 32;

            if (_target.m_BakedConvex != null && _target.m_BakedConvex.Length >= 4)
                RT.Occluder.ConvexHull3d(_target.m_BakedConvex, out m_ConvexVertex, out m_ConvexPolygons, 0);
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            GUILayout.Space(8);

            EditorGUILayout.BeginHorizontal();
            m_TargetCount = EditorGUILayout.IntSlider("", m_TargetCount, 8, 64);
            if (GUILayout.Button("Bake"))
            {
                SetConvexOccluder _target = (SetConvexOccluder)target;
                Undo.RecordObject(target, "Bake");

                Vector3[] vertices = _target.GetComponent<MeshFilter>().sharedMesh.vertices;
                int[] hull;
                int[] edge;

                RT.Occluder.ConvexHull3d(vertices, out m_ConvexVertex, out m_ConvexPolygons, m_TargetCount);
                RT.Occluder.GetVolumeEdgeAndHull(m_ConvexPolygons, out hull, out edge);

                SceneView.RepaintAll();
                _target.m_BakedConvex = m_ConvexVertex;
                _target.m_BakedHullTriangle = hull;
                _target.m_BakedEdge = edge;
            }
            if (GUILayout.Button("Reset"))
            {
                SetConvexOccluder _target = (SetConvexOccluder)target;
                Undo.RecordObject(target, "Reset");
                _target.m_BakedConvex = null;
                _target.m_BakedHullTriangle = null;
                _target.m_BakedEdge = null;
                m_ConvexVertex = null;
                SceneView.RepaintAll();
            }
            EditorGUILayout.EndHorizontal();
        }

        private void OnSceneGUI()
        {
            if (m_ConvexVertex != null && m_ConvexVertex.Length > 0 && m_ConvexPolygons != null)
            {
                SetConvexOccluder _target = (SetConvexOccluder)target;
                Vector3[] vertex = new Vector3[m_ConvexVertex.Length];
                List<Vector3> back_lines = new List<Vector3>();
                List<Vector3> front_lines = new List<Vector3>();
                for (int i = 0; i < vertex.Length; i++)
                {
                    vertex[i] = _target.transform.TransformPoint(m_ConvexVertex[i]);
                }
                for (int i = 0; i < m_ConvexPolygons.Length;)
                {
                    Vector3 nrm = Vector3.Cross(vertex[m_ConvexPolygons[i + 2]] - vertex[m_ConvexPolygons[i]], vertex[m_ConvexPolygons[i + 1]] - vertex[m_ConvexPolygons[i]]);
                    Plane p = new Plane(Vector3.Normalize(nrm), vertex[m_ConvexPolygons[i]]);
                    SceneView sceneview = SceneView.currentDrawingSceneView;
                    bool front = p.GetDistanceToPoint(sceneview.camera.transform.position) > 0;

                    List<Vector3> list = front ? front_lines : back_lines;
                    int v0, v1;
                    for (v0 = v1 = m_ConvexPolygons[i++]; i < m_ConvexPolygons.Length; i++)
                    {
                        int v2 = m_ConvexPolygons[i];
                        if (v2 == -1)
                        {
                            list.Add(vertex[v1]);
                            list.Add(vertex[v0]);
                            i++;
                            break;
                        }
                        else
                        {
                            list.Add(vertex[v1]);
                            list.Add(vertex[v2]);
                            v1 = v2;
                        }
                    }
                    Handles.color = Color.gray;
                    Handles.DrawLines(back_lines.ToArray());
                    Handles.color = Color.white;
                    Handles.DrawLines(front_lines.ToArray());
                }
            }
        }
    }
#endif
}
1 Like

OK, thanks, I will check the code and wait for the store update, or request you by email if it will be takes too long :wink:

Hello.
I am looking for a working Dynamic Occlusion Culling system. Can you answer two questions?
1.Does this asset work on the Console platform?
2.This asset is described as not using GPU, but does it support multi-threaded CPU processing? Is CPU main thread usage minimal?
Thank you.

@ohbado

  1. There is no platform limit.
  2. It is currently not operating in parallel like a general unity script. We plan to review the relevant issues.
    The calculated load depends on the number of objects. There is a variable that controls the calculation.

1.5 Updated.
It can add a window to the side of the occluder. (like a wall with door.)

Does this support concave shapes? Also does it support runtime manipulation, addition, and deletion of the occluder shapes?

Concave is not supported, only Convex is supported.
(However, it supports that the occluder is overlapped by multiple occluders. Hole on surface like a door are restrictively supported.)
It is not possible flexibly change the shape, it is just possible to add and remove occluders runtime.

Does this plug-in support Webgl?

check post #7

See the sample page below.
https://noerror.github.io/rtoccdemo/

Hi I bought your asset, Im quite satisfied with it, though I have a future feature request, i thought already inculed: frustum culling. Right now only the occluded objects get culled, that are inside the camera frustum, all of the objects that are behind the player are still active.

With that feature implemented this asset is easily 5 stars.

Do you mean something like this? (I tried to modify the code as below.)

code
I’ll try to be able to selectively apply it to the next update.
Currently we are preparing for optimization using compute shaders.

1 Like

Well, yes. This is what i meant, wonderful job. I think that you should update your example videos too, because this is much more impressive this way. Thank you for your update. I rated the asset 5*