2
\$\begingroup\$

In my cube world, I want to use octrees to represent my chunks of 20x20x20 cubes for frustum and occlusion culling. I understand how octrees work, I just dont know if I'm going about this the right way.

My base octree class is taken from here: http://www.xnawiki.com/index.php/Octree. The link is now dead but the code can be found below.

What I'm wondering is how to apply occlusion culling using this class. Does it make sense to have one octree for each cube chunk? Or should I make the octree bigger? Since I'm using cubes, each cube should fit into a node without overlap so that won't be an issue

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;


namespace Game
{

//    Copyright (C) 2011 Daniel Rosser
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program. See /LICENSES/gpl-3.0.txt
//    If not, see <http://www.gnu.org/licenses/>.
//
//    Daniel Rosser <danoli3@gmail.com> 



    public class Octree
    {
        private const int maxObjectsInNode = 4;
        private const float minSize = 4.0f;        

        // center coordinate point for the octree
        private Vector3 center;
        // size of the octree space
        private float size;

        List<Entity> objects;
        private BoundingBox bounds = default(BoundingBox);

        Octree parent = null;
        Octree nodeUFL;
        Octree nodeUFR;
        Octree nodeUBL;
        Octree nodeUBR;
        Octree nodeDFL;
        Octree nodeDFR;
        Octree nodeDBL;
        Octree nodeDBR;
        List<Octree> children;

        public static int modelsDrawn;
        private static int modelsStoredinOctreeTree;
        private static int modelsFullyContainedInNodes;
        private static int modelsIntersectionsInNodes;

        private static String collisionName;

        public int ModelsDrawn { get { return modelsDrawn; } set { modelsDrawn = value; } }
        public int ModelsInOctree { get { return modelsStoredinOctreeTree; } set { modelsStoredinOctreeTree = value; } }
        public int ModelsFullyContainedInNodes { get { return modelsFullyContainedInNodes; } set { modelsFullyContainedInNodes = value; } }
        public int ModelsIntersectionsInNodes { get { return modelsIntersectionsInNodes; } set { modelsIntersectionsInNodes = value; } }



        public static int modelsInNodes;        
        public int ModelsInNodes { get { return modelsInNodes; } set { modelsInNodes = value; } }

        /// <summary>
        /// Octree - Creates a spatial partitioning octree
        /// </summary>
        /// <param name="center">Center of the Octree</param>
        /// <param name="size">How large is the octree</param>
        /// <param name="parent">The parent node (if not the root)</param>
        public Octree(Vector3 center, float size, Octree parent = null)
        {
            this.parent = parent;
            this.center = center;
            this.size = size;
            objects = new List<Entity>();
            children = new List<Octree>(8);

            Vector3 diagonalVector = new Vector3(size / 2.0f, size / 2.0f, size / 2.0f);
            bounds = new BoundingBox(center - diagonalVector, center + diagonalVector);

        }

        /// <summary>
        /// Return the Size of the Octree
        /// </summary>
        /// <returns>float</returns>
        public float WorldSize()
        {
            return size;
        }

        /// <summary>
        /// Returns the 8 children of this node so one can iterate over them
        /// </summary>
        /// <returns>List of Octree Nodes</returns>
        public List<Octree> GetChildren()
        {
            return children;
        }

        /// <summary>
        /// NeedSplit - Does this Node need to be split (max objects contained)
        /// </summary>
        private Boolean NeedSplit
        {
            get
            {
                return (objects.Count != 0 && maxObjectsInNode < objects.Count && size >= minSize);
            }
        }
        /// <summary>
        /// Is this Node Empty
        /// </summary>
        protected Boolean IsEmpty
        {
            get
            {
                return ((objects.Count == 0) && (HasChildren == false));
            }
        }

        /// <summary>
        /// Are the children nodes emtpy
        /// </summary>
        protected Boolean AreChildrenEmpty
        {
            get
            {
                if (HasChildren == false)
                    return true;

                for (int i = 0; i < 8; ++i)
                {
                    if (!children[i].IsEmpty)
                        return false;
                }
                return true;
            }
        }

        /// <summary>
        /// Does this Node have children
        /// </summary>
        private Boolean HasChildren
        {
            get
            {
                return (children.Count != 0);
            }
        }


        /// <summary>
        /// What type of containment is this bounding box to this
        /// </summary>
        /// <param name="bsbounds">ref bouding box</param>
        /// <returns>ContainmentType (Disjoint, Intersects, Contained)</returns>
        protected ContainmentType Containment(ref BoundingBox bbbounds)
        {
            ContainmentType containmentType;
            bounds.Contains(ref bbbounds, out containmentType);
            return (containmentType);
        }
        /// <summary>
        /// What type of containment is this bounding sphere to this
        /// </summary>
        /// <param name="bsbounds">ref bouding sphere</param>
        /// <returns>ContainmentType (Disjoint, Intersects, Contained)</returns>
        protected ContainmentType Containment(ref BoundingSphere bsbounds)
        {
            ContainmentType containmentType;
            bounds.Contains(ref bsbounds, out containmentType);
            return (containmentType);
        }


        /// <summary>
        /// Can this Node contain this bounding sphere
        /// </summary>
        /// <param name="bsbounds">ref bouding sphere</param>
        /// <returns>Boolean</returns>
        protected Boolean CanContain(ref BoundingSphere bsbounds)
        {           
            return (ContainmentType.Contains == Containment(ref bsbounds));
        }
        /// <summary>
        /// Can this Node contain this bounding box
        /// </summary>
        /// <param name="bsbounds">ref bouding box</param>
        /// <returns>Boolean</returns>
        protected Boolean CanContain(ref BoundingBox bbounds)
        {            
            return (ContainmentType.Contains == Containment(ref bbounds));
        }
        /// <summary>
        /// Can this bounding box intersect
        /// </summary>
        /// <param name="bbounds">ref bounding box</param>
        /// <returns>Boolean</returns>
        protected Boolean CanIntersect(ref BoundingBox bbounds)
        {            
            return (ContainmentType.Intersects == Containment(ref bbounds));
        }
        /// <summary>
        /// Can this bounding sphere intersect
        /// </summary>
        /// <param name="bbounds">ref bounding box</param>
        /// <returns>Boolean</returns>
        protected Boolean CanIntersect(ref BoundingSphere bsbounds)
        {
            return (ContainmentType.Intersects == Containment(ref bsbounds));
        }
        /// <summary>
        /// Can this Octree node contain or intersect this Entity
        /// </summary>
        /// <param name="entity">Object Entity</param>
        /// <returns>Boolean</returns>
        protected Boolean CanContainOrIntersect(Entity entity)
        {
            if (entity.collision == Entity.Collision.BoundingSphere)
                return CanContainOrIntersect(entity.boundsphere);
            else
                return CanContainOrIntersect(entity.bounds);            

        }

        protected Boolean CanContainOrIntersect(BoundingBox bbounds)
        {
            if (ContainmentType.Disjoint == Containment(ref bbounds))
                return false;
            else
                return true;
        }
        protected Boolean CanContainOrIntersect(BoundingSphere bsbounds)
        {
            if (ContainmentType.Disjoint == Containment(ref bsbounds))
                return false;
            else
                return true;
        }

        /// <summary>
        /// Can this Octree Node contain this entity
        /// </summary>
        /// <param name="entity">Object Entity</param>
        /// <returns>Boolean</returns>
        public Boolean CanContain(Entity entity)
        {
            if(entity.collision == Entity.Collision.BoundingSphere)
                return CanContain(ref entity.boundsphere);
            else            
                return CanContain(ref entity.bounds);
        }

        public Boolean containsObjects()
        {
            if (objects.Count != 0)
                return true;

            if (children.Count != 0)
            {

                for (int index = 0; index < children.Count; index++)
                {
                    if (children[index].containsObjects() == true)
                        return true;
                }
            }

            return false;
        }


        public void Add(Entity entity)
        {
            entity.ID = modelsStoredinOctreeTree++;            
            AddObject(entity);
        }

        /// <summary>
        /// Find the First Parent of the Entity
        /// </summary>
        /// <param name="entity">Object Entity</param>
        /// <returns>Octree</returns>
        private Octree FindEntityParent(Entity entity)
        {
            Octree octree = null;
            for (int index = 0; index < objects.Count; index++)
            {
                if (objects[index] == entity)
                {
                    return this;
                }
            }

            int child = 0;
            while ((octree == null) && (child < children.Count))
            {
                octree = children[child++].FindEntityParent(entity);
                if (octree != null)
                    break;
            }

            return octree;
        }

        /// <summary>
        /// Remove Entity based on its ID and return it // TODO add recursive
        /// </summary>
        /// <param name="modelID"></param>
        /// <returns></returns>
        private Entity RemoveEntity(int modelID)
        {
            Entity entity = null;

            for (int index = 0; index < objects.Count; index++)
            {
                if (objects[index].ID == modelID)
                {
                    entity = objects[index];
                    objects.Remove(entity);
                    Collapse(this);
                    return entity;
                }
            }

            int child = 0;
            while ((entity == null) && (child < children.Count))
            {
                entity = children[child++].RemoveEntity(modelID);
                if (entity != null)
                    break;
            }

            return entity;
        }

        private void RemoveObject(int objectIndex, Boolean merge = true)
        {
            // remove the object            
            objects.RemoveAt(objectIndex);
            modelsInNodes--;

            if (merge == true)
            {
                // if all the children are empty lets clear the list
                if (AreChildrenEmpty)
                    children.Clear();

                // try and collapse / merge the Octree by notifying the parent to check other children
                if (IsEmpty)
                {
                    if (parent != null)
                        parent.AttemptMerge(this);
                }
            }
        }

        public Boolean RemoveObject(Entity octreeEntity)
        {
            if (octreeEntity == null) { throw new NullReferenceException("octreeEntity"); }

            if (!CanContain(octreeEntity)) // Object is definitely not in this tree
                return false;

            if (RemoveObjectHere(octreeEntity))//was pressent in this node
            {
                return true;
            }
            else //check child nodes
            {
                if (children.Count == 0)
                    return false;

                foreach (Octree child in children)
                {
                    if (child.RemoveObject(octreeEntity))
                        return true;
                }
            }

            return false;
        }

        public void RemoveObjectRecursively(Entity octreeEntity)
        {
            if (octreeEntity == null) { throw new NullReferenceException("octreeEntity"); }           

            RemoveObjectHere(octreeEntity, false);
            if (children.Count != 0)
            {
                foreach (Octree child in children)
                    child.RemoveObjectRecursively(octreeEntity);                
            }
        }

        protected Boolean RemoveObjectHere(Entity octreeEntity, Boolean merge = true)
        {
            if (objects.Count == 0)
                return false;       // no objects to remove            

            int objIndex = objects.IndexOf(octreeEntity);
            if (-1 == objIndex)
                return false;

            octreeEntity.InsideNodes--;
            RemoveObject(objIndex, merge);
            return true;
        }

        protected void AttemptMerge(Octree abandonedChild)
        {
            if (AreChildrenEmpty)
            {
                children.Clear();

                if (parent != null && objects.Count == 0)
                {
                    parent.AttemptMerge(this);
                }
            }
        }

        public void Collapse(Octree octree)
        {
            int childrenNumber = octree.children.Count;
            if (childrenNumber != 0)
            {
                int counter = 0;
                for (int index = 0; index < childrenNumber; index++)
                {
                    if (octree.children[index].containsObjects() == false && octree.parent != null)
                    {
                        octree.children[index].Collapse();
                        counter++;
                    }
                }
                if (counter == childrenNumber)
                {
                    for (int index = (childrenNumber - 1); index >= 0; index--)
                    {
                        octree.children.Remove(octree.children[index]);
                    }
                    //octree.children.Clear();
                }

            }

            if (octree.objects.Count > 0)
                return;
            else if (octree.children.Count > 0)
                return;
            else
            {
                if (octree.parent != null)
                {
                    octree.parent.Collapse(octree.parent);
                }
            }

        }

        public void Collapse()
        {
            int childrenNumber = children.Count;
            if (childrenNumber != 0)
            {
                int counter = 0;
                for (int index = 0; index < childrenNumber; index++)
                {
                    if (children[index].containsObjects() == false)
                    {
                        children[index].Collapse();
                        if (parent != null)
                            counter++; // meaning we don't remove the top octree root
                    }
                }
                if (counter == childrenNumber)
                {
                    for (int index = (childrenNumber - 1); index >= 0; index--)
                    {
                        children.Remove(children[index]);
                    }
                    //children.Clear();
                }

            }

        }


        public Boolean AddObject(Entity octreeEntity)
        {
            if (octreeEntity == null) { throw new NullReferenceException("octreeEntity"); }

            if (!CanContainOrIntersect(octreeEntity))
                return false;

            //if (!CanContain(octreeEntity) || 

            if (!HasChildren || NeedSplit) // If no children
            {
                AddObjectHere(octreeEntity);

                if (NeedSplit)
                {
                    CreateChildren();
                    DistributeObjectsToChildren();
                }
            }
            else
            {
                if (!AddObjectToChildren(octreeEntity))
                {
                    AddObjectHere(octreeEntity);
                }
            }

            return true;
        }

        protected void AddObjectHere(Entity octreeEntity)
        {
            modelsInNodes++;
            octreeEntity.InsideNodes++;
            // debug remove in production
            if (CanContain(octreeEntity) == true)
                modelsFullyContainedInNodes++;
            if (CanIntersect(ref octreeEntity.bounds) == true)
                modelsIntersectionsInNodes++;
            objects.Add(octreeEntity);
        }


        private void CreateChildren()
        {
            float sizeOver2 = size / 2.0f;
            float sizeOver4 = size / 4.0f;

            nodeUFR = new Octree(center + new Vector3(sizeOver4, sizeOver4, -sizeOver4), sizeOver2, this);
            nodeUFL = new Octree(center + new Vector3(-sizeOver4, sizeOver4, -sizeOver4), sizeOver2, this);
            nodeUBR = new Octree(center + new Vector3(sizeOver4, sizeOver4, sizeOver4), sizeOver2, this);
            nodeUBL = new Octree(center + new Vector3(-sizeOver4, sizeOver4, sizeOver4), sizeOver2, this);
            nodeDFR = new Octree(center + new Vector3(sizeOver4, -sizeOver4, -sizeOver4), sizeOver2, this);
            nodeDFL = new Octree(center + new Vector3(-sizeOver4, -sizeOver4, -sizeOver4), sizeOver2, this);
            nodeDBR = new Octree(center + new Vector3(sizeOver4, -sizeOver4, sizeOver4), sizeOver2, this);
            nodeDBL = new Octree(center + new Vector3(-sizeOver4, -sizeOver4, sizeOver4), sizeOver2, this);

            children.Add(nodeUFR);
            children.Add(nodeUFL);
            children.Add(nodeUBR);
            children.Add(nodeUBL);
            children.Add(nodeDFR);
            children.Add(nodeDFL);
            children.Add(nodeDBR);
            children.Add(nodeDBL);
        }        

        protected void DistributeObjectsToChildren()
        {
            if (children.Count != 0 && objects.Count != 0)
            {
                for (int i = objects.Count - 1; i >= 0; --i)
                {
                    Entity octreeObject = objects[i];
                    if (AddObjectToChildren(octreeObject))
                    {
                        RemoveObject(i);
                    }
                    //else
                        //AddObjectHere(octreeObject); // it is left in the parent
                }
            }
        }

        protected Boolean AddObjectToChildren(Entity entity)
        {
            if (children.Count != 0)
            {
                for (int index = 0; index < children.Count; index++)
                {
                    if (children[index].CanContain(entity) == true)
                    {
                        children[index].AddObject(entity);
                        return true;
                    }
                }
                Boolean added = false;
                int counter = 0;
                for (int index = 0; index < children.Count; index++)
                {
                    if (children[index].Containment(ref entity.bounds) == ContainmentType.Intersects)                    
                        counter++;
                }
                if (counter == children.Count)
                    return false;

                for (int index = 0; index < children.Count; index++)
                {
                    if (children[index].Containment(ref entity.bounds) == ContainmentType.Intersects)
                    {
                        children[index].AddObject(entity);
                        added = true;
                    }

                }
                return added;
            }

            return false;
        }

        protected void AddObjectToParent(Entity octreeEntity)
        {
            if (octreeEntity == null) { throw new NullReferenceException("octreeEntity null"); }
            if (!AddObject(octreeEntity))
            {
                if (parent != null)
                    parent.AddObjectToParent(octreeEntity);
            }
        }

        /// <summary>
        /// Check if the Ray intersects the Octree node
        /// </summary>
        /// <param name="ray"></param>
        /// <returns></returns>
        private Boolean CheckRay(Ray ray)
        {
            if (ray.Intersects(this.bounds) != null)
            {
                return true;
            }
            else
                return false;
        }         


        public Boolean ContainsDynamic(BoundingBox bounds)
        {
            if (bounds == null) { throw new NullReferenceException("bounds null"); }
            List<Octree> containers = new List<Octree>(FindContainers(bounds));
            collisionNodesTraversedLast = containers.Count;
            if (containers.Count != 0)
            {
                foreach (Octree octree in containers)
                {
                    if (octree.objects.Count != 0)
                    {
                        collisionObjectCompaisions = 0;
                        foreach (Entity comparedEntity in octree.objects)
                        {
                            if (comparedEntity.CheckCollision(bounds) == true && comparedEntity.type == Entity.Type.DYNAMIC)
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }

        public Boolean ContainsStatic(BoundingBox bounds)
        {            
            if (bounds == null) { throw new NullReferenceException("bounds null"); }
            List<Octree> containers = new List<Octree>(FindContainers(bounds));
            collisionNodesTraversedLast = containers.Count;
            if (containers.Count != 0)
            {
                foreach (Octree octree in containers)
                {
                    if (octree.objects.Count != 0)
                    {
                        collisionObjectCompaisions = 0;
                        foreach (Entity comparedEntity in octree.objects)
                        {                              
                              if (comparedEntity.CheckCollision(bounds) == true && comparedEntity.type == Entity.Type.STATIC)
                              {
                                  return true;
                              }                            
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// If ObjectChanged Update the node that it is located in (as it has tranformed in space and possibly moved nodes)
        /// </summary>
        /// <param name="octreeEntity"></param>
        public void ObjectChanged(Entity octreeEntity)
        {
            if (octreeEntity == null) { throw new NullReferenceException("octreeEntity null"); }
            Octree octree = FindEntityParent(octreeEntity);

            if (octree != null)
            {
                if(octree.parent != null)
                    octree.parent.RemoveObjectRecursively(octreeEntity); //destroy all instances of this entity
                else
                    octree.RemoveObjectRecursively(octreeEntity);

                if (octreeEntity.InsideNodes != 0)
                    this.RemoveObjectRecursively(octreeEntity);

                if (!octree.CanContain(octreeEntity)) // if the current top node cant contain the object
                {
                    if (octree.parent != null) // if parent is not null
                    {
                        if (octree.parent.CanContain(octreeEntity)) // if the parent can contain
                        {
                            if (!octree.parent.AddObject(octreeEntity))
                            { throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to parent node"); }
                        }
                        else if (octree.parent.parent != null) 
                        {
                            if (octree.parent.parent.CanContain(octreeEntity)) // if the parents parent can contain (2 branches up as max, else be better putting into root)
                            {
                                if (!octree.parent.parent.AddObject(octreeEntity))
                                { throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to parent.parent node"); }
                            }
                            else
                            {
                                if (!this.AddObject(octreeEntity))
                                { throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to the root octree"); }
                            }
                        }
                        else
                        {
                            if (!this.AddObject(octreeEntity))
                            { throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to the root octree"); }
                        }
                    }
                    else
                    {
                        if (!this.AddObject(octreeEntity))
                        { throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to the root octree"); }
                    }
                }
                else
                    if (!octree.AddObject(octreeEntity))
                        throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added back to its current node");
            }
            else
            {
                //throw new NullReferenceException("Octree: ObjectChanged(): Parent of changed object was null? Debug this!");
                if (!this.AddObject(octreeEntity))
                    throw new NullReferenceException("Octree: ObjectChanged(): Object could not be added to the root octree!!!???? out of range?");
            }

            if(octree != null)
                Collapse(octree);


        }


        /// <summary>
        /// Draw
        /// </summary>
        /// <param name="viewMatrix">View Matrix</param>
        /// <param name="projectionMatrix">Project Matrix</param>
        /// <param name="cameraFrustrum">Bounding Camera Frustrum</param>
        public void Draw(Matrix viewMatrix, Matrix projectionMatrix, BoundingFrustum cameraFrustrum)
        {
            ContainmentType cameraNodeContainment = cameraFrustrum.Contains(bounds);
            if (cameraNodeContainment != ContainmentType.Disjoint)
            {
                if (objects.Count != 0)
                {
                    foreach (Entity entity in objects)
                    {
                        if (entity.drawn == false)
                        {
                            entity.Draw(viewMatrix, projectionMatrix);
                            modelsDrawn++;
                            entity.drawn = true;
                        }
                    }
                }

                foreach (Octree childNode in children)
                    childNode.Draw(viewMatrix, projectionMatrix, cameraFrustrum);
            }
        }

        public void DrawBoxLines(Matrix viewMatrix, Matrix projectionMatrix, GraphicsDevice device, BasicEffect basicEffect)
        {
            foreach (Octree childNode in children)
                childNode.DrawBoxLines(viewMatrix, projectionMatrix, device, basicEffect);

            if (children.Count == 0)
                Utilities.DrawBoundingBox(bounds, device, basicEffect, Matrix.Identity, viewMatrix, projectionMatrix);
        }

        public void DrawZoneOfDeath(Matrix viewMatrix, Matrix projectionMatrix, GraphicsDevice device, BasicEffect basicEffect)
        {
            Vector3 diagonalVector = new Vector3(2, 2, 2);            
            Utilities.DrawBoundingBox(new BoundingBox(center - diagonalVector, center + diagonalVector), device, basicEffect, Matrix.Identity, viewMatrix, projectionMatrix, Color.Orange);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ What problems do you foresee with occlusion culling? - Perhaps add this to your question. \$\endgroup\$ – Engineer Oct 17 '13 at 18:05
  • 2
    \$\begingroup\$ one octree for each cube chunk would be not very useful, since ostensibly each chunk would be one buffer. You'd want to cull chunks, not individual blocks. \$\endgroup\$ – Jimmy Oct 17 '13 at 18:09
  • \$\begingroup\$ I cant get a 20x20x20 chunk on the screen without some frame rate dropping unless i cull individual blocks. I did occlusion culling but the completely wrong way \$\endgroup\$ – Christian Frantz Oct 17 '13 at 18:10
  • \$\begingroup\$ Why do you want you want to use octrees? What benefit do you see from this? Do you want fine voxels? Or is there some other reason? \$\endgroup\$ – Engineer Oct 17 '13 at 18:16
  • 4
    \$\begingroup\$ @ChristianFrantz If you can't draw 8,000 (20x20x20) cubes on screen without occlusion culling and without your frame rate dropping then you might have bigger issues right now than octrees can fix - that should be well within the range of any hardware without substantial trouble. \$\endgroup\$ – Steven Stadnicki Oct 17 '13 at 18:18
1
\$\begingroup\$

For occlusion culling with an octree, you could build low-res hierarchical depth buffer first from potential occluders (taking max depth of 4 samples for lower-res LOD) and test the octree against it. You need to determine the octree node size in screen and pick the proper LOD to test against to reduce the test to 4 samples per octree node.

The main issue is how you build the initial occlusion buffer to test the octree against without rendering everything. You could: 1) Hand pick larger models and render them to the buffer 2) Reproject previous frame depth (only stationary objects), but this introduces holes so you would need to patch it somehow 3) Render objects which were visible last frame, but you need some way to determine which objects were visible (e.g. render object ID buffer)

Since you said you are using instancing, this could be done entirely in GPU side by building an instance stream thus you don't need to figure out how to get the rasterized depth buffer to CPU (without latency issues). You might be better off not using octree though but just throwing all the instances in FOV to the shader which does the occlusion test and builds your visible instance stream.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

This question deals with the same problem, even if its c++ & opengl most of the concept apply to your case too. There is also a good general article on Gamasutra regarding visibility culling.

For your special case you could construct an octree, where the leaves contain batches of cubes and than use bounding boxes to check if a region is visible or not.

Just to make sure if you are familiar with the general concept: Occlusion Culling with octree works this way: You develop a simplified visibility check and divide your word into an octree. You start on a higher level and move down the tree. If a branch isn´t visible all the sub branches and leaves can be discarded too.

Using the class given, you could probably make use of the CanContainOrIntersect method to test for visibility.

But I also agree with Steven, if you have problems with 20x20x20 cubes you should definitely look into some other optimizations.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.