Node Visibility

From K-3D

Jump to: navigation, search

Overview

Currently, the render engine plugins in K-3D (OpenGL, RenderMan, and Yafray) render a document by iterating over every node in the document, querying each node for the appropriate render-engine-specific interface, and if the node implements that interface, calling the appropriate method(s). In the OpenGL and RenderMan cases, the node is then responsible for rendering itself, using APIs appropriate to the render engine.

Because nodes (usually) render themselves, they have the option of doing nothing when called. Nodes have visibility properties that they test before rendering themselves - in most cases, several visibility properties, one for each type of render engine. In the case of the RenderMan engine, nodes have two visibility-related properties, one controlling visibility of the object in the final rendered frame, and one controlling visibility of the object when generating shadow maps.

Although this system is easy to understand and straightforward to implement, it has many drawbacks:

  • Each render pass iterates over all N document nodes instead of (the ideal) M visible nodes. The difference may be significant, particularly during interactive rendering, since time is wasted querying nodes that either aren't renderable at all, aren't renderable by that particular render engine, or aren't currently visible.
  • Redundant dynamic casts - the work performed to query each node for a rendering interface is performed for every render pass. This is repetitive, potentially expensive, and may have a significant impact during interactive rendering.
  • Users pay for what they don't use - the visibility flags in renderable nodes consume memory, CPU cycles, and disk space whether they're ever used by the user or not.
  • Can't handle multiple render engine types - adding one-or-more visibility flags for every type of render engine added to the system will not scale for two reasons:
    • A long laundry-list of visibility flags becomes unusable for users (and exacerbates the problem of paying for what they don't use).
    • Adding a new render engine type means touching (even if only through a base class) every drawable node in the system.
  • Can't handle multiple render passes - in the general case, users will need to control the visibility of renderable nodes on a per-render-pass (i.e. per-render-engine) basis. The current visibility flags cannot be extended to this case.

Proposal

Maintain lists of renderable nodes on a per-render-engine basis. This makes it possible to:

  • Control visibility on a per-render-engine basis, by simply adding/removing nodes from each render engine's list of nodes-to-be rendered.
  • Improve performance in multiple ways:
    • Render engines only have to iterate over their individual list of visible nodes, rather than every node in the document.
    • Render engines no longer have to query nodes for render interfaces during each render pass, their internal list of visible nodes can already contain the correct interface types.
  • Improve scalability in functionality - a suitably-smart user interface layer should be able to control node visibility for new render engine types without any coding.
  • Support for multi-pass rendering - provides fine-grained control over which render engine(s) will render a given node.
  • Users don't pay for what they don't use - the overhead of multiple visibility properties per-node is eliminated.

Challenges

  • We will likely need to create new property types to handle lists of nodes.
    • To eliminate dynamic casting, the property value type will have to represent lists of specific interface types, not just k3d::inode pointers.
      • This may make it difficult to create a user interface that "just works" regardless of interface types.
  • Controlling node visibility through the pipeline will require additional special-purpose nodes.
Personal tools