Using UVAtlas

Many rendering and content generation techniques require a unique, non-overlapping map of a 2D signal (such as a texture) onto a mesh. Such techniques include:

Generating a unique UV mapping manually is often time-consuming and tedious; this is especially true when the input geometry is complex and efficient/low-distortion texture-space utilization is desired.

Figure 1: An example mesh and its corresponding texture atlas

This example shows a mesh (on the left) and the corresponding UV-space normal map (on the right). Notice that the texture atlas contains several groups or clusters of data; each cluster is called a chart and in the example above, displays contains the normal data for a portion of the mesh.

The D3DX UVAtlas APIs automatically generate an optimal, non-overlapping texture atlas. The APIs provide input parameters that allow you to:

How UVAtlas Works

The UVAtlas APIs generate a texture atlas by partitioning a surface into charts and packing the charts into a texture atlas. Use D3DXUVAtlasPartition and D3DXUVAtlasPack to perform these steps separately; or use D3DXUVAtlasCreate to partition, parameterize and pack in a single call.

Partitioning and Parameterizing a Mesh

First, the mesh is partitioned into charts, then each chart is parameterized into its own [0,1] UV-space. A cylinder can be parameterized by one chart; a sphere on the other hand will require two charts.

Figure 2: A sphere partitioned into two charts

A mesh which can be parameterized with a single chart is classified as "homeomorphic to a disk", meaning you could spread out an infinitely flexible, infinitely stretchable disk over the chart and cover the geometry perfectly. This stretching, called a homeomorphism, is a bidirectional function; which means you can go from one parameterization to the other without losing information.

Very few real-world meshes can be parameterized into two dimensions without separating the mesh into clusters, or charts.

Figure 3: Another example mesh and its corresponding texture atlas

There are two parameters that determine the number of charts created:

The amount of stretch will determine the number of charts that are generated, and the overall quality of the sampling. Stretch ranges from 0.0 (no stretch) to 1.0 (any amount of stretch). D3DXUVAtlasCreate and D3DXUVAtlasPartition return the maximum stretch generated by the algorithm.

Figure 4: Another example mesh and its corresponding texture atlas

Using Integrated Metric Tensors to Control Parameterization

Texture-space prioritization can be specified on a per-triangle basis. Integrated Metric Tensors can be provided to control how triangles are stretched in the resulting texture-space atlas. IMT's can be specified directly or computed based on an input signal using the D3DX IMT computation functions. An integrated metric tensor (or IMT) is a symmetrical 2x2 matrix that describes how a triangle is stretched in the atlas. Each IMT is defined by 3 floats, call them (a,b,c). They can be arranged in a symmetric 2x2 matrix like this:

a b
b c

Then the IMT can be used to find the distance between two vectors. Given two vectors v1 and v2, where :

vector v1
vector v2 = v1 + (s,t)

The distance between v1 and v2 can be calculated as:

sqrt((s, t) * M * (s, t)^T)

In other words, the vector (s,t) could be the magnitude of the stretch in an arbitrary direction in u-v space. In this case, the s vector is a direction from the first to the second vertex, and t is the cross product of the normal and s. For instance:

(1,1) * (1,1) = (2,2)
        (1,1)
IMT(1,1,1) scales by 2
(1,-1) * (1,1) = (0,0)
         (1,1)
IMT(2,0,2) scales by 2 with no shearing

IMT's can be specified directly or computed based on an input signal using the D3DX IMT computation functions: D3DXComputeIMTFromPerVertexSignal, D3DXComputeIMTFromPerTexelSignal, D3DXComputeIMTFromSignal, and D3DXComputeIMTFromTexture_graphics.

Specify IMT data directly if you want to control how texture-space is allocated to individual triangles. By doing so, allocate more area in the atlas to important areas of a mesh (such as a character's face or chest logo, or regions of a scene near a player's walking-path). By specifying IMT's that are multiples of the identity matrix, the resulting triangles will be scaled uniformly in texture space.

For example, given a high-resolution normal map, you can compute IMT to provide more texture-space to areas of higher frequency signal in the normal map. Triangles that are "flat" (that mapped to constant regions of the original normal map) will receive less texture space. Triangles that contain a great deal of normal-map detail will receive more texture area in the final result. You can then resample the normal map into a smaller texture but maintain detail, or you can recompute the normal map with the more optimal UV mapping.

Using Adjacency Data for User Specified Creases

User-defined adjacency information can be provided to the partitioning function to describe pre-defined creases in the mesh, and thus define a chart boundary between adjacent faces. This is a simple way for the caller to specify their own chart partitioning as input into the algorithm, which will further refine charts to bring the stretch under the maximum allowed.

Example

This example illustrates how you might use the UVAtlas APIs and the DirectX Viewer to find and fix discontinuities in your model that can dramatically affect the size of your texture atlas.

Assume you started with some model in your favorite content generation software (this example uses a dwarf head model that was created in Maya). Export the textured model to an .x file and create a texture atlas with D3DXUVAtlasCreate. The resulting texture atlas would look something like this:

Figure 5: Example atlas for a dwarf model

The atlas has 22 charts and a maximum stretch of 0.994.

Now look at the textured model to see how well the texture atlas maps to the geometry. To do this, load the model into the viewer tool:

Here's what you should see:

Figure 6: Textured Mesh in the Viewer tool

Each line is a crease which is an adjacent edge between two charts in the texture atlas. The number of charts generated by the algorithm is caused by slight differences perhaps due to discontinuities in the normals. These small differences can be reduced by welding data, that is, forcing data that is nearly equal to be equal. To weld the normals and the skinweights:

This compares the normals and skinweights, and where they differ in value by less than 2.01, the data is made equal. This figure shows a close up of the eye to see the creases before welding (on the left) and the creases after welding (on the right):

Figure 7: Removing creases by welding

In this example, welding removed 86 vertices from the input mesh. With fewer creases in the mesh, you can regenerate the atlas:

Figure 8: New atlas with creases removed

The atlas only has 7 charts and a maximum stretch of approximately 0.0776. The new atlas now fits into a smaller texture (approximately 30% smaller in this example).

Packing Charts Into an Atlas

Once a mesh has been partitioned into individually-parameterized charts, the charts need to be packed efficiently into a single texture map. This is performed as the second step of D3DXUVAtlasCreate or can be invoked explicitly by calling D3DXUVAtlasPack.

Packed charts are separated by a user-specified gutter width. The gutter width is the amount of separation between charts, and allows for bilinear interpolation and mip-mapping to avoid rendering artifacts at chart boundaries. D3DX provides an interface for automatically filling in these gutters - see ID3DXTextureGutterHelper for more information.

Integrating UVAtlas Into Your Pipeline

In addition to being artist-invoked prior to texture painting, these functions can be integrated into an automated art pipeline. For example, a UVAtlas call can be issued automatically after an asset is updated, prior to performing a PRT simulation or normal mapping pass. This avoids any need to manually manual repair of an object's UV mapping if the mesh's topology has been modified.

See the UV Atlas Command-Line Tool (uvatlas.exe) for example usage of the UVAtlas functions.