Charles Petzold

3D Non-Affine Transforms

August 6, 1007
Roscoe, N.Y

Traditionally, the two-dimensional graphics environments for Windows have been limited to affine transforms — that is, 2D transforms defined by the following matrix (using property names in the WPF Matrix structure) :


The third column of the matrix is pre-set and unavailable. The generalized transform formulas are:

In WPF 3D, the Matrix3D structure defines a 4×4 transform matrix with the following property names:


The fourth column is available to application programs, which means that in addition to the normal 3D affine transforms that look like this:

WPF 3D also allows non-affine transforms that look like this:

These are called non-affine transforms because they could result in infinite values if the denominator equals zero. By default, the M14, M24, and M34 properties equal zero and M44 equals 1.

The division in the transform formulas is necessary to move the 4D coordinates that normally result from a 4×4 matrix transform back into 3D space. (And don't worry if this sounds whacky: Chapter 7 of my new book 3D Programming for Windows has an extensive discussion of the mechanics, rationale, and peculiarities of linear, affine, and non-affine transforms in 2D and 3D space, and why it's necessary to get 4D space involved as well.)

The following XAML file displays a simple unit cube:

By default, the camera points at the cube head on, but you can use the scrollbars on the bottom and right to rotate the camera to get a better view:

Normally I define unit cubes so they're centered on the origin. However, this one is defined with Y values of 0 and 1 so it sits on top of the XZ plane. I've done this in preparation for applying a non-affine transform. Although the GeometryModel3D in PlainCube.xaml has a MatrixTransform3D applied to it, the Matrix3D contains all default values. (It's not necessary for the Matrix3D element to be defined in the XAML file in this way. An alternative approach lets you assign a Matrix attribute of the MatrixTransform3D element with a single string of 16 numbers.)

The following file is the same as PlainCube.xaml except it uses the Matrix3D element to define a non-affine transform:

The matrix I've defined is this:


The transform formulas are:

At the base of the cube, where y equals 0, the formulas represent an identity transform. However, at the top of the cube, where y equals 1, both x' and z' are 1/10th of the values of x and z, but y' equals y (which is 1). The result is a frustum (or truncated pyramid):

This tapering effect is characteristic of non-affine matrix transforms, and, in fact, non-affine transforms are used internally in WPF 3D to simulate the effect of perspective. (Details about the 3D camera transforms are also in Chapter 7 of my book.) Affine transforms always map parallel lines to parallel lines; non-affine transforms do not.

When using non-affine transforms, it helps to plan ahead and try to anticipate what you want and what you'll see. It's very easy to define a non-affine transform that results in infinite coordinates or something otherwise not visible. In fact, I shifted my unit cube to have positive Y coordinates to make my life easier. If values of Y in the cube ranged from -0.5 to 0.5, the non-affine transform I defined would have zero values at -1/9.

The ability to set the last column in the WPF 3D transform matrix doesn't provide much of a variety of different effects, but it certainly comes in handy for simple tapering jobs.

Buy my book and we'll both be happy! quantumbooks
Barnes & Noble Amazon Canada Amazon UK
Amazon Français Amazon Deutsch Amazon Japan