Charles Petzold

Normalization of TextureCoordinates

December 14, 2006
Roscoe, NY

In WPF 3D, when applying a 2D brush to a 3D object, you specify how the 2D coordinates of the brush correspond to the 3D coordinates of the object with the TextureCoordinates property of the MeshGeometry3D class. The "3-D Graphics Overview" (online here) states "TextureCoordinates are specified as a value between zero and 1, inclusive." In other words, they are relative brush coordinates, with (0,0) being the upper-left corner of the brush and (1,1) being the lower-right corner.

In reality, however, if the values in the TextureCoordinates collection are not between 0 and 1, then they are internally normalized to be between 0 and 1. For example, if the X values of the TextureCoordinates points range from -27 to 5, then all the X values are internally increased by 27 and divided by 32.

This doesn't mean that the entire rectangular image gets applied to the 3D object, because only triangles are extracted from the image to be applied to the triangles of the MeshGeometry3D. But it does mean that at least one point on all four sides of the image gets on the 3D object (unless you fake it out in some way).

A small XAML file demonstrates this. The same bitmap is applied as an ImageBrush to three triangles using very small and very large TextureCoordinates values, and the end result is the same.

I discovered this peculiarity when attempting to use TextureCoordinates to select only a portion of an ImageBrush to use on a 3D object. It just would not work. Every TextureCoordinates I picked gave me the whole image! It took a bit of time for reality to sink in.

So, how do you select only a portion of a bitmap ImageBrush to use? Set the Viewbox property that ImageBrush inherits from TileBrush. For example, you can add this attribute to the ImageBrush tag defined as a resource in this XAML file:

It's a Rect. A subset of the image is selected that has an upper-left coordinate of (0.35, 0.10) with a width of 0.5 and a height of 0.4. Of course, if the bitmap is being applied to a triangular 3D object (as it is in this example), then corners of the image will be lobbed off.