mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-03 08:21:03 +00:00
4513cdcbac
Added information about recent PatchParam changes as well as some discussion of Single-Crease patches, Local Points, and Legacy Gregory patches.
534 lines
23 KiB
ReStructuredText
534 lines
23 KiB
ReStructuredText
..
|
|
Copyright 2013 Pixar
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
with the following modification; you may not use this file except in
|
|
compliance with the Apache License and the following modification to it:
|
|
Section 6. Trademarks. is deleted and replaced with:
|
|
|
|
6. Trademarks. This License does not grant permission to use the trade
|
|
names, trademarks, service marks, or product names of the Licensor
|
|
and its affiliates, except as required to comply with Section 4(c) of
|
|
the License and to reproduce the content of the NOTICE file.
|
|
|
|
You may obtain a copy of the Apache License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the Apache License with the above modification is
|
|
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the Apache License for the specific
|
|
language governing permissions and limitations under the Apache License.
|
|
|
|
|
|
FAR Overview
|
|
------------
|
|
|
|
.. contents::
|
|
:local:
|
|
:backlinks: none
|
|
|
|
.. image:: images/api_layers_3_0.png
|
|
:width: 100px
|
|
:target: images/api_layers_3_0.png
|
|
|
|
Feature Adaptive Representation (Far)
|
|
=====================================
|
|
|
|
The *Far* API layer is the central interface that processes client-supplied
|
|
geometry and turns it into a `serialized data representation
|
|
<api_overview.html#multiple-representations>`__ ready for parallel processing.
|
|
|
|
First, *Far* provides the tools to refine subdivision topology
|
|
(`Far::TopologyRefiner <#far-topologyrefiner>`__). Topology refinement can be
|
|
either uniform or sparse, where extraordinary features are automatically
|
|
isolated (see `feature adaptive subdivision <subdivision_surfaces.html#feature-adaptive-subdivision>`__).
|
|
|
|
As a geometry representation, *Far* also provides a set of *"Table"* classes.
|
|
These tables are designed to be static containers for the refined topology
|
|
data, after it has been serialized and factorized. This representation is
|
|
embodied in the `Far::PatchTable <#far-patchtable>`__ and the
|
|
`Far::StencilTable <#far-patchtable>`__ classes.
|
|
|
|
*Far* is also a fully featured API. Typically *Far* tabular data is targeted at
|
|
*Osd*, where it can be processed by an implementation optimized for a specific
|
|
hardware. However, for client-code that does not require a dedicated
|
|
implementation, *Far* itself provides a fully-featured single-threaded
|
|
implementation of subdivision interpolation algorithms, both discrete and at
|
|
the limit.
|
|
|
|
Refining Topology
|
|
=================
|
|
|
|
The *Far* topology classes present a public interface for the refinement
|
|
functionality provided in *Vtr*, either directly within Far or indirectly
|
|
eventually though *Osd*. The two main topology refinement classes are as
|
|
follows:
|
|
|
|
+-------------------------------+---------------------------------------------------+
|
|
| TopologyRefiner | A class encapsulating the topology of a refined |
|
|
| | mesh. |
|
|
+-------------------------------+---------------------------------------------------+
|
|
| TopologyRefinerFactory<MESH> | A factory class template specialized by users (in |
|
|
| | terms of their mesh class) to construct |
|
|
| | TopologyRefiner as quickly as possible. |
|
|
+-------------------------------+---------------------------------------------------+
|
|
|
|
These classes are the least well defined of the API, but given they provide the
|
|
public interface to all of the improvements proposed, they potentially warrant
|
|
the most attention. Far::TopologyRefiner is purely topological and it is the
|
|
backbone used to construct or be associated with the other table classes in Far.
|
|
|
|
|
|
Far::TopologyRefiner
|
|
********************
|
|
|
|
TopologyRefiner is the building block for many other useful classes in
|
|
OpenSubdiv, but its purpose is more specific. It is intended to store the
|
|
topology of an arbitrarily refined subdivision hierarchy to support the
|
|
construction of `stencil table <#patch-table>`__, `patch table
|
|
<#patch-table>`__, etc.
|
|
|
|
Aside from public access to topology, TopologyRefiner has public refinement
|
|
methods (currently *RefineUniform()* and *RefineAdapative()*) where simple
|
|
specifications of refinement will be translated into refinement operations
|
|
within Vtr. Feature-adaptive refinement is a special case of *"sparse"* or
|
|
*"selective"* refinement, and so the feature-adaptive logic exists internal
|
|
to TopologyRefiner and translates the feature-analysis into a simpler
|
|
topological specification of refinement to Vtr.
|
|
|
|
.. image:: images/topology_refiner.png
|
|
:align: center
|
|
|
|
The longer term intent is that the public Refine...(...) operations eventually
|
|
be overloaded to allow clients more selective control of refinement. While
|
|
TopologyRefiner is a purely topological class, and so free of any definitions
|
|
of vertex data, the public interface has been extended to include templated
|
|
methods that allow clients to interpolate primitive variable data.
|
|
|
|
Far::TopologyRefinerFactory
|
|
***************************
|
|
|
|
Consistent with other classes in Far, instances of TopologyRefiner are created
|
|
by a factory class -- in this case Far::TopologyRefinerFactory. This class
|
|
is an important entry point for clients its task is to map/convert data in a
|
|
client's mesh into the internal `Vtr <vtr_overview.html>`__ representation as
|
|
quickly as possible.
|
|
|
|
The TopologyRefinerFactory class is a class template parameterized by and
|
|
specialized for the client's mesh class, i.e. TopologyRefinerFactory<MESH>.
|
|
Since a client' mesh representation knows best how to identify the topological
|
|
neighborhoods required, no generic implementation would provide the most
|
|
direct means of conversion possible, and so we rely on specialization. For
|
|
situations where mesh data is not defined in a boundary representation, a
|
|
simple container for raw mesh data is provided (TopologyDescriptor) along
|
|
with a Factory specialized to construct TopologyRefiners from it.
|
|
|
|
So there are two ways to create TopologyRefiners:
|
|
|
|
* use the existing TopologyRefinerFactory<TopologyDescriptor> with a
|
|
populated instance of TopologyDescriptor
|
|
* specialize TopologyRefinerFactory<class MESH> for more efficient
|
|
conversion
|
|
|
|
TopologyDescriptor is a simple struct with pointers to raw mesh data in a
|
|
form common to mesh constructors. Topologically, the minimal requirement
|
|
consists of:
|
|
|
|
* the number of vertices and faces of the mesh
|
|
* an array containing the number of vertices per face
|
|
* an array containing the vertices assigned to each face
|
|
|
|
These last two define one of the six topological relations that are needed
|
|
internally by Vtr, but this one relation is sufficient to construct the rest.
|
|
Additional members are available to assign sharpness values per edge and/or
|
|
vertex, hole tags to faces, or to define multiple sets (channels) of
|
|
face-varying data.
|
|
|
|
Specialization of TopologyRefinerFactory<class MESH> should be done with care
|
|
as the goal here is to maximize the performance of the conversion and so
|
|
minimize overhead due to runtime validation. The template provides the
|
|
high-level construction of the required topology vectors of the underlying
|
|
Vtr. It requires the specification/specialization of two methods with the
|
|
following purpose:
|
|
|
|
* specify the sizes of topological data so that vectors can be pre-allocated
|
|
* assign the topological data to the newly allocated vectors
|
|
|
|
As noted above, the assumption here is that the client's boundary-rep knows best
|
|
how to retrieve the data that we require most efficiently. After the factory class
|
|
gathers sizing information and allocates appropriate memory, the factory provides
|
|
the client with locations of the appropriate tables to be populated (using the
|
|
same `Array <vtr_overview.html#arry-type>`__ classes and interface used to access
|
|
the tables). The client is expected to load a complete topological description
|
|
along with additional optional data, i.e.:
|
|
|
|
* the six topological relations required by Vtr, oriented when manifold
|
|
* sharpness values for edges and/or vertices (optional)
|
|
* additional tags related to the components, e.g. holes (optional)
|
|
* values-per-face for face-varying channels (optional)
|
|
|
|
While there is plenty of opportunity for user error here, that is no different
|
|
from any other conversion process. Given that Far controls the construction
|
|
process through the Factory class, we do have ample opportunity to insert
|
|
runtime validation, and to vary that level of validation at any time on an
|
|
instance of the Factory.
|
|
|
|
A common base class has been created for the factory class, i.e.:
|
|
|
|
.. code:: c++
|
|
|
|
template <class MESH>
|
|
class TopologyRefinerFactory : public TopologyRefinerFactoryBase
|
|
|
|
both to provide common code independent of <MESH> and also potentially to
|
|
protect core code from unwanted specialization.
|
|
|
|
Far::PatchTable
|
|
================
|
|
|
|
|
|
The patch table is a serialized topology representation. This container is
|
|
generated using *Far::PatchTableFactory* from an instance
|
|
*Far::TopologyRefiner* after a refinement has been applied. The
|
|
FarPatchTableFactory traverses the data-structures of the TopologyRefiner and
|
|
serializes the sub-faces into collections of bi-linear and bi-cubic patches as
|
|
dictated by the refinement mode (uniform or adaptive). The patches are then
|
|
sorted into arrays based on their types.
|
|
|
|
.. container:: impnotip
|
|
|
|
**Release Notes (3.0.0)**
|
|
|
|
The organization and API of Far::PatchTable is likely to change
|
|
in the 3.1 release to accommodate additional functionality including:
|
|
smooth face-varying interpolation on patches, and dynamic feature
|
|
adaptive isolation (DFAS), and patch evaluation of Loop subdivision
|
|
surfaces.
|
|
|
|
Patch Arrays
|
|
************
|
|
|
|
The patch table is a collection of control vertex indices. Meshes are decomposed
|
|
into a collection of patches, which can be of different types. Each type
|
|
has different requirements for the internal organization of its
|
|
control-vertices. A PatchArray contains a sequence of multiple patches that
|
|
share a common set of attributes.
|
|
|
|
While all patches in a PatchArray will have the same type, each patch in the
|
|
array is associated with a distinct *PatchParam* which specifies additional
|
|
information about the individual patch.
|
|
|
|
Each PatchArray contains a patch *Descriptor* that provides the fundamental
|
|
description of the patches in the array.
|
|
|
|
The PatchArray *ArrayRange* provides the indices necessary to track the records
|
|
of individual patches in the table.
|
|
|
|
.. image:: images/far_patchtables.png
|
|
:align: center
|
|
:target: images/far_patchtables.png
|
|
|
|
Patch Types
|
|
***********
|
|
|
|
The following are the different patch types that can be represented in the
|
|
PatchTable:
|
|
|
|
+---------------------+------+---------------------------------------------+
|
|
| Patch Type | #CVs | Description |
|
|
+=====================+======+=============================================+
|
|
| NON_PATCH | n/a | *"Undefined"* patch type |
|
|
+---------------------+------+---------------------------------------------+
|
|
| POINTS | 1 | Points : useful for cage drawing |
|
|
+---------------------+------+---------------------------------------------+
|
|
| LINES | 2 | Lines : useful for cage drawing |
|
|
+---------------------+------+---------------------------------------------+
|
|
| QUADS | 4 | Bi-linear quads-only patches |
|
|
+---------------------+------+---------------------------------------------+
|
|
| TRIANGLES | 3 | Bi-linear triangles-only mesh |
|
|
+---------------------+------+---------------------------------------------+
|
|
| LOOP | n/a | Loop patch (currently unsupported) |
|
|
+---------------------+------+---------------------------------------------+
|
|
| REGULAR | 16 | B-spline Basis patches |
|
|
+---------------------+------+---------------------------------------------+
|
|
| GREGORY | 4 | Legacy Gregory patches |
|
|
+---------------------+------+---------------------------------------------+
|
|
| GREGORY_BOUNDARY | 4 | Legacy Gregory Boundary patches |
|
|
+---------------------+------+---------------------------------------------+
|
|
| GREGORY_BASIS | 20 | Gregory Basis patches |
|
|
+---------------------+------+---------------------------------------------+
|
|
|
|
|
|
The type of a patch dictates the number of control vertices expected in the
|
|
table as well as the method used to evaluate values.
|
|
|
|
Patch Parameterization
|
|
**********************
|
|
|
|
Each patch represents a specific portion of the parametric space of the
|
|
coarse topological face identified by the PatchParam FaceId. As topological
|
|
refinement progresses through successive levels, each resulting patch
|
|
corresponds to a smaller and smaller subdomain of the face.
|
|
The PatchParam UV origin describes the mapping from the uv domain of the
|
|
patch to the uv subdomain of the topological face. We encode this uv
|
|
origin using log2 integer values for compactness and efficiency.
|
|
|
|
It is important to note that this uv parameterization is the intrinsic
|
|
parameterization within a given patch or coarse face and is distinct
|
|
from any client specified face-varying channel data.
|
|
|
|
Patches which result from irregular coarse faces (non-quad faces in the
|
|
Catmark scheme, or non-trianglular faces in the Loop scheme) are offset
|
|
by the one additional level needed to "quadrangulate" or "triangulate"
|
|
the irregular face.
|
|
|
|
.. image:: images/far_patchUV.png
|
|
:align: center
|
|
:target: images/far_patchUV.png
|
|
|
|
A patch along an interpolated boundary edge is supported by an incomplete
|
|
sets of control vertices. For consistency, patches in the PatchTable always
|
|
have a full set of control vertex indices and the PatchParam Boundary bitmask
|
|
identifies which control vertices are incomplete (the incomplete control
|
|
vertex indices are assigned values which duplicate the first valid index).
|
|
Each bit in the boundary bitmask corresponds to one edge of the patch
|
|
starting from the edge from the first vertex and continuing around the
|
|
patch. With feature adaptive refinement, regular B-spline basis patches
|
|
along interpolated boundaries will fall into one of the eight cases
|
|
(four boundary and four corner) illustrated below:
|
|
|
|
.. image:: images/far_patchBoundary.png
|
|
:align: center
|
|
:target: images/far_patchBoundary.png
|
|
|
|
Transition edges occur during feature adaptive refinement where a patch
|
|
at one level of refinement is adjacent to pairs of patches at the next
|
|
level of refinement. These T-junctions do not pose a problem when evaluating
|
|
primvar data on patches, but they must be taken into consideration when
|
|
tessellating patches (e.g. while drawing) in order to avoid cracks.
|
|
The PatchParam Transition bitmask identifies the transition edges of
|
|
a patch. Each bit in the bitmask corresponds to one edge of the patch
|
|
just like the encoding of boundary edges.
|
|
|
|
After refining an arbitrary mesh, any of the 16 possible transition edge
|
|
configurations might occur. The method of handling transition edges is
|
|
delegated to patch drawing code.
|
|
|
|
.. image:: images/far_patchTransition.png
|
|
:align: center
|
|
:target: images/far_patchTransition.png
|
|
|
|
Single-Crease Patches
|
|
**************************
|
|
|
|
Using single-crease patches allows a mesh with creases to be represented
|
|
with many fewer patches than would be needed otherwise. A single-crease
|
|
patch is a variation of a regular BSpline patch with one additional crease
|
|
sharpness parameter.
|
|
|
|
.. container:: impnotip
|
|
|
|
**Release Notes (3.0.0)**
|
|
|
|
Currently, the crease sharpness parameter is encoded as a separate
|
|
PatchArray within the PatchTable. This parameter may be combined
|
|
with the other PatchParam values in future releases. Also, evaluation
|
|
of single-crease patches is currently only implemented for OSD patch
|
|
drawing, but we expect to implement support in all of the evaluation
|
|
code paths for future releases.
|
|
|
|
Local Points
|
|
************
|
|
|
|
The control vertices represented by a PatchTable are primarily refined points,
|
|
i.e. points which result from applying the subdivision scheme uniformly or
|
|
adaptively to the points of the coarse mesh. However, the final patches
|
|
generated from irregular faces, e.g. patches incident on an extraordinary
|
|
vertex might have a representation which requires additional local points.
|
|
|
|
.. container:: impnotip
|
|
|
|
**Release Notes (3.0.0)**
|
|
|
|
Currently, representations which require local points also require
|
|
the use of a StencilTable to compute the values of local points.
|
|
This requirement, as well as the rest of the API related to local
|
|
points may change in future releases.
|
|
|
|
Legacy Gregory Patches
|
|
**********************
|
|
|
|
Using Gregory patches to approximate the surface at the final patches
|
|
generated from irregular faces is an alternative representation which does
|
|
not require any additional local points to be computed. Instead, when
|
|
Legacy Gregory patches are used, the PatchTable must also have an alternative
|
|
representation of the mesh topology encoded as a vertex valence table
|
|
and a quad offsets table.
|
|
|
|
.. container:: impnotip
|
|
|
|
**Release Notes (3.0.0)**
|
|
|
|
The encoding and support for Legacy Gregory patches may change
|
|
in future releases. The current encoding of the vertex valence
|
|
and quad offsets tables may be prohibitively expensive for some
|
|
use cases.
|
|
|
|
Far::StencilTable
|
|
==================
|
|
|
|
The base container for stencil data is the StencilTable class. As with most
|
|
other Far entities, it has an associated StencilTableFactory that requires a
|
|
TopologyRefiner:
|
|
|
|
Advantages
|
|
**********
|
|
|
|
Stencils are used to factorize the interpolation calculations that subdivision
|
|
schema apply to vertices of smooth surfaces. If the topology being subdivided
|
|
remains constant, factorizing the subdivision weights into stencils during a
|
|
pre-compute pass yields substantial amortizations at run-time when re-posing
|
|
the control cage.
|
|
|
|
Factorizing the subdivision weights also allows to express each subdivided
|
|
vertex as a weighted sum of vertices from the control cage. This step effectively
|
|
removes any data inter-dependency between subdivided vertices : the computations
|
|
of subdivision interpolation can be applied to each vertex in parallel without
|
|
any barriers or constraint. The Osd::Compute module leverages these properties
|
|
on massively parallel GPU architectures to great effect.
|
|
|
|
.. image:: images/far_stencil5.png
|
|
:align: center
|
|
|
|
Principles
|
|
**********
|
|
|
|
Iterative subdivision algorithms converge towards the limit surface by
|
|
successively refining the vertices of the coarse control cage. Each successive
|
|
iteration interpolates the new vertices by applying polynomial weights to a
|
|
*basis of supporting vertices*.
|
|
|
|
The interpolation calculations for any given vertex can be broken down into
|
|
sequences of multiply-add operations applied to the supporting vertices.
|
|
Stencil table encodes a factorization of these weighted sums : each stencils is
|
|
created by combining the list of control vertices from the 1-ring.
|
|
|
|
With iterative subdivision, each refinement step is dependent upon the previous
|
|
subdivision step being completed, and a substantial number of steps may be
|
|
required in order approximate the limit : each subdivision step incurs an
|
|
O(4\ :superscript:`n`) growing amount of computations.
|
|
|
|
Instead, once the weights of the contributing coarse control vertices for a
|
|
given refined vertex have been factorized, it is possible to apply the stencil
|
|
and directly obtain the interpolated vertex data without having to process the
|
|
data for the intermediate refinement levels.
|
|
|
|
.. image:: images/far_stencil7.png
|
|
:align: center
|
|
|
|
Cascading Stencils
|
|
******************
|
|
|
|
Client-code can control the amount of factorization of the stencils : the tables can be
|
|
generated with contributions all the way from a basis of coarse vertices, or
|
|
reduced only to contributions from vertices from the previous level of
|
|
refinement.
|
|
|
|
The latter mode allows client-code to access and insert modifications to the
|
|
vertex data at set refinement levels (see `hierarchical vertex edits
|
|
<subdivision_surfaces.html#hierarchical-edits>`_). Once the edits have been
|
|
applied by the client-code, another set of stencils can be used to smoothe the
|
|
vertex data to a higher level of refinement.
|
|
|
|
.. image:: images/far_stencil8.png
|
|
:align: center
|
|
|
|
See implementation details, see the Far cascading stencil `tutorial
|
|
<tutorials.html>`_
|
|
|
|
Limit Stencils
|
|
**************
|
|
|
|
Stencil tables can be trivially extended from discrete subdivided vertices to
|
|
arbitrary locations on the limit surface. Aside from extraordinary points, every
|
|
location on the limit surface can be expressed as a closed-form weighted
|
|
average of a set of coarse control vertices from the 1-ring surrounding the
|
|
face.
|
|
|
|
The weight accumulation process is similar : the control cage is adaptively
|
|
subdivided around extraordinary locations. A stencil is then generated for each
|
|
limit location simply by factorizing the bi-cubic Bspline patch weights over
|
|
those of the contributing basis of control-vertices.
|
|
|
|
The use of bi-cubic patches also allows the accumulation of analytical
|
|
derivatives, so limit stencils carry a set of weights for tangent vectors.
|
|
|
|
.. image:: images/far_stencil0.png
|
|
:align: center
|
|
|
|
Once the stencil table has been generated, limit stencils are the most direct
|
|
and efficient method of evaluation of specific locations on the limit of a
|
|
subdivision surface, starting from the coarse vertices of the control cage.
|
|
|
|
Also: just as discrete stencils, limit stencils that are factorized from coarse
|
|
control vertices do not have inter-dependencies and can be evaluated in
|
|
parallel.
|
|
|
|
For implementation details, see the `glStencilViewer <glStencilViewer.html>`_
|
|
code example.
|
|
|
|
Sample Location On Extraordinary Faces
|
|
**************************************
|
|
|
|
Each stencil is associated with a singular parametric location on the coarse
|
|
mesh. The parametric location is defined as face location and local [0.0 - 1.0]
|
|
(u,v) triplet:
|
|
|
|
In the case of face that are not quads, a parametric sub-face quadrant needs to
|
|
be identified. This can be done either explicitly or implicitly by using the
|
|
unique ptex face indices for instance.
|
|
|
|
.. image:: images/far_stencil6.png
|
|
:align: center
|
|
|
|
Code example
|
|
************
|
|
|
|
When the control vertices (controlPoints) move in space, the limit locations can
|
|
be very efficiently recomputed simply by applying the blending weights to the
|
|
series of coarse control vertices:
|
|
|
|
.. code:: c++
|
|
|
|
class StencilType {
|
|
public:
|
|
|
|
void Clear() {
|
|
memset( &x, 0, sizeof(StencilType));
|
|
}
|
|
|
|
void AddWithWeight( StencilType const & cv, float weight ) {
|
|
x += cv.x * weight;
|
|
y += cv.y * weight;
|
|
z += cv.z * weight;
|
|
}
|
|
|
|
float x,y,z;
|
|
};
|
|
|
|
std::vector<StencilType> controlPoints,
|
|
points,
|
|
utan,
|
|
vtan;
|
|
|
|
// Update points by applying stencils
|
|
controlStencils.UpdateValues<StencilType>( reinterpret_cast<StencilType const *>(
|
|
&controlPoints[0]), &points[0] );
|
|
|
|
// Update tangents by applying derivative stencils
|
|
controlStencils.UpdateDerivs<StencilType>( reinterpret_cast<StencilType const *>(
|
|
&controlPoints[0]), &utan[0], &vtan[0] );
|
|
|