Skip to content
Lindsay Kay edited this page Sep 9, 2013 · 47 revisions
Distant bodies culled Body comes closer Body detail increases Body detail level increase more

Contents

Overview

SceneJS supports detail culling via a special frustum/lod node type, which sets up an axis-aligned bounding box around its child nodes. Each child provides a different level of detail for the object within the boundary, and the ```frustum/lod`` enables exactly one of these children at any instant to show the appropriate level of detail, based on the current projected 2D canvas size of the boundary.

The ```frustum/body`` node type can be configured to cull the children from view when the boundary falls outside the view frustum. This is an optional configuration because the node might potentially be used to switch detail for non-visible objects, such as sound sources, which could influence the scene even when they fall outside the frustum. Note that the node disables all children at the lowest level of detail, effectively working as a sort of distance culling.

Internally, culling is distributed across multiple culling systems, each running in a separate web worker. This attempts to make culling efficient while preventing it from starving the main rendering thread.

Creating a Detail Culling Body

The frustum/lod node encloses its child nodes in a World-space bounding box and enables the appropriate child for the box's current projected 2D canvas size.

As shown in the example below, we configure the node with a 2D size threshold for each child node, given in property sizes. When the boundary's projected size falls below the first of these thresholds, then no child will be enabled. When the projected size is between the first and second thresholds, the first child is enabled, when between the second and third thresholds, the second child is enabled, and so on.

An unlimited number of child nodes and size thresholds are supported.

By default, all of the children are disabled when the bounding box falls outside the view frustum, but using the optional frustumCull property we can disable that behaviour in order to cull the children solely on the projected box size, which actually works regardless of which direction the box is in with regard to the viewing direction. This is useful for disabling distant sound effects etc.

Example:

someNode.addNode({
   type: "frustum/lod",

   // World-space axis-aligned bounding box
   min: [xPos - 8, yPos - 3, zPos - 5],
   max: [xPos + 8, yPos + 6, zPos + 5],

   // Size thresholds, one for each child node, in ascending 
   // order for size, unlimited number
   sizes: [
      50, 250, 350 // More as required... 
   ],

   // Option to show the boundary as a wireframe box for debugging - default false
   showBoundary: false,

   // Option to disable all child nodes when the bounding box 
   // falls outside the view frustum - true by default
   frustumCull: true,

   // A child node for each size, in ascending order of 
   // detail, unlimited number
   nodes: [

      // Detail level #1 (lowest)
      // A light blue box

      {
         type: "material",
         color: {
            r: 0.6,
            g: 0.6,
            b: 1.0
         },

         nodes: [

            {
               type: "translate",
               x: xPos,
               y: yPos,
               z: zPos,

               nodes: [{
                  type: "scale",
                  x: 4,
                  y: 3,
                  z: 4,
                  nodes: [

                     // Box primitive, implemented by plugin at
                     // http://scenejs.org/api/latest/plugins/node/prims/box.js
                     {
                        type: "prims/box"
                     }
                  ]
               }]
            }
         ]
      },

      // Detail level #2 (middle)
      // A medium-blue sphere

      {
         type: "material",
         color: {
            r: 0.5,
            g: 0.5,
            b: 1.0
         },

         nodes: [{
            type: "translate",
            x: xPos,
            y: yPos,
            z: zPos,

            nodes: [{
               type: "scale",
               x: 1.0,
               y: 0.7,
               z: 1.0,

               nodes: [

                  // Sphere primitive, implemented by plugin at
                  // http://scenejs.org/api/latest/plugins/node/prims/sphere.js
                  {
                     type: "prims/sphere",
                     radius: 5,
                     latitudeBands: 16, // A fairly low-rez sphere
                     longitudeBands: 16
                  }
               ]
            }]
         }]
      },

      // Detail level #3 (highest)
      // A dark blue teapot

      {
         type: "material",
         color: {
            r: 0.3,
            g: 0.3,
            b: 1.0
         },

         nodes: [{
            type: "translate",
            x: xPos,
            y: yPos - 2.5,
            z: zPos,

            nodes: [{
               type: "scale",
               x: 2.5,
               y: 2.5,
               z: 2.5,

               nodes: [

                  // Teapot primitive, implemented by plugin at
                  // http://scenejs.org/api/latest/plugins/node/prims/teapot.js
                  {
                     type: "prims/teapot"
                  }
               ]
            }]
         }]
      }

      // Mode children as required..
   ]
});

Implementation

  • The frustum/lod node is implementated by this custom node plugin.
  • For performance, culling is distributed across multiple culling engines, each running within its own worker thread. Each of those workers is proxied by a system in which the frustum/lod nodes create frustum collision bodies, and those systems are managed in a load-balanced pool.
  • The frustum/lod node actually wraps a more basic frustum/body node type, which creates the collision body and subscribes to it's frustum intersection status and projected 2D boundary size. The frustum/lod node does the job of translating those updates into child node enables/disables. This strategy allows us to reuse the frustum/body to create new types of culling node.

Roadmap

Clone this wiki locally