I am rendering the Sponza scene ato test my frustum implementation.

My current approach is simply, test if the vertices of the triangle are within the frustum (angle between the camera looking direction and the position od the vertex relative to the camera).

If any of the vertices are visible then the trianlge is considered to be visible.

The issue is that the sponza scene has some really large triangles, thus with my approach it is possible to be looking directly at a triangle and see nothing if all 3 vertices are outside the camera frustum.


Elimination of large triangles isn't as simple as the vertices aren't on screen then the triangle can't be seen.

For an accurate frustum cull, the only valid elimination scenario for your data (there are more deeper versions) is that the all vertices are on one side of a frustum plane. The reason for this is that your triangle may straddle the frustum (ala cross the plane of your screen).

Testing the angle between the vertices and the camera position is also inaccurate, as your frustum isn't a point, its a projection of a box. Therefore you need to test your vertices against 6 planes that make up your frustum.

Finally, testing every vertex against the frustum will become expensive. This is where abstracting your data becomes more important. Usually abstraction comes in the form of using bounding boxes to group up sets of triangles (also known as a mesh) to quickly eliminate them from your potential visible set.

I've attached my frustum generation (create at start of every frame) code and the bounding box test:

  void GenerateFrustumPlanes()
            if (m_disableFrustumUpdates)

            Matrix matrix;

            // Create the frustum matrix from the view matrix and updated projection matrix.
            matrix = m_wvp;

            m_planes[0] = new Vector4(matrix.M13,
                matrix.M43); // near
            m_planes[1] = new Vector4(matrix.M14 - matrix.M13,
                matrix.M24 - matrix.M23,
                matrix.M34 - matrix.M33,
                matrix.M44 - matrix.M43);

            m_planes[2] = new Vector4(matrix.M14 + matrix.M11,
                matrix.M24 + matrix.M21,
                matrix.M34 + matrix.M31,
                matrix.M44 + matrix.M41); // left
            m_planes[3] = new Vector4(matrix.M14 - matrix.M11,
                matrix.M24 - matrix.M21,
                matrix.M34 - matrix.M31,
                matrix.M44 - matrix.M41);//right

            m_planes[4] = new Vector4(matrix.M14 - matrix.M12,
                matrix.M24 - matrix.M22,
                matrix.M34 - matrix.M32,
                matrix.M44 - matrix.M42); // top
            m_planes[5]  = new Vector4(matrix.M14 + matrix.M12, matrix.M24 + matrix.M22, matrix.M34 + matrix.M32, matrix.M44 + matrix.M42); // bottom

            for (int i = 0; i < s_frustumPlanes; i++)

Testing code:

public bool IsInViewableArea(List<Vector3> a_cornerLIst)
            // this is a simpler occlusion test where we basically don't even need to test frustum as the the extents are outside the camera view distance.  But you need to test extents + width because we only hold 2 corners and squares are 4

            int iTotalIn = 0;

            Vector4[] pList = GetFrustumPlanes();  // this should only be called once.

            // test all 8 corners against the 6 sides 
            // if all points are behind 1 specific plane, we are out
            // if we are in with all points, then we are fully in
            Vector4 temp;
            float dotvector;
            for (int p = 0; p < Camera3d.s_frustumPlanes; ++p)

                int iInCount = 8;
                int iPtIn = 1;

                for (int i = 0; i < a_cornerLIst.Count; ++i)
                    //Vector3 a = pList[i].
                    // test this point against the planes
                    //   if (pList[p].SideOfPlane(vList[i]) == BEHIND)
                    temp = (Vector4)(a_cornerLIst[i] );
                    temp.W = 1;

                    dotvector = Vector4.Dot(pList[p], temp);

                    if (dotvector < 0.0f)
                        iPtIn = 0;

                // were all the points outside of plane p?
                if (iInCount == 0)
                    return false;
                // check if they were all on the right side of the plane
                iTotalIn += iPtIn;

            // so if iTotalIn is 6, then all are inside the view
            //    if (iTotalIn == 6)
            //       return true;

            // we must be partly in then otherwise
            return true;
  • \$\begingroup\$ I am also doing this on the GPU on the geometry shader, which is probably not a good idea. How big of a performance hit is it vs doing it directly on the GPU? \$\endgroup\$
    – Makogan
    Jun 18 '18 at 23:19
  • \$\begingroup\$ The elimination is best done for starters in the CPU, if you group your data into more manageable chunks such as bounding boxes. You could just test 3 instead of 8 items in your cornerlist, just set the iInCount to 3 instead to get the desired solution. The way I know for using the GPU is to use a compute shader firstly to test the datasets/frustrum, then feed this into the geometry pipeline to just render. This thread on gamedev has some discussions on why and why not also -> gamedev.net/forums/topic/664472-frustum-culling \$\endgroup\$
    – ErnieDingo
    Jun 18 '18 at 23:25
  • \$\begingroup\$ It also highlights that if you are using triangles against the frustrum, its actually clipping. But that is a discussion for another time and place....\ \$\endgroup\$
    – ErnieDingo
    Jun 18 '18 at 23:26
  • \$\begingroup\$ It's Frustum, not FrustRum ;-) \$\endgroup\$
    – Kromster
    Jun 19 '18 at 6:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .