3D matrices in WPF and Silverlight

3D matrices in WPF and Silverlight

To handle 3D transformations, WPF has high-level classes (TranslateTransform3D, ScaleTransform3D and RotateTransform3D).

Silverlight does not include the 3D engine of WPF, but has since version 3 a projection system based on the Projection abstract class. The high-level PlaneProjection class inherits from it and can easily represent a perspective view of an UIElement.

But for various reasons, in WPF and in Silverlight, it may be interesting to use the lower-level technique of matrices. For educational purposes first. But also to keep reflexes acquired with frameworks such as Direct3D or OpenGL.

First let introduce the 3D coordinate systems.

3D Coordinate Systems

The three axes of a 3D coordinate system can be represented through the fingers of one hand : the thumb represents the x-axis, the index finger the y-axis and the middle finger the z-axis. The axes are perpendicular to each other. The x-axis is usually directed towards the right and the y-axis upwards. The direction of the z-axis differenciates two different systems : the "right-handed" system and the "left-handed" system.

In the "right-handed" system, the z-axis is directed forward (it leaves the screen). Moreover, in this system, a rotation according to a positive angle value around an axis is always in a counter-clockwise direction (right hand folded around the axis, thumb in the positive direction (*)). The "right-handed" is used by WPF and OpenGL.

Right-handed system

In "left-handed" system, the z-axis is directed backwards. A rotation according to a positive angle value around an axis is always in a clockwise direction (left hand folded around the axis, thumb in the positive direction (*)). This system is used by Direct3D, POV-Ray and in some projections (upon which we will return).

left-handed system

(*) The POV-Ray documentation shows how to determine with the hand the rotation direction of a positive angle.

3D Matrices

A matrix is an array of values that defines a transformation of coordinates.

In WPF and Silverlight, the Matrix3D structure represents a 3D matrix, with 4 columns and 4 rows :

M11M12M13M14
M21M22M23M24
M31M32M33M34
OffsetX (M41)OffsetY (M42)OffsetZ (M43)M44

M11, M12, M13, M14, M21, M22, M23, M24, M31, M32, M33, M34, OffsetX, OffsetY, OffsetZ and M44 are properties of the Matrix3D structure (M41, M42 and M43 don't exist as properties).

A transformation can be applied to a point by multiplying it by a matrix. under WPF, it is also possible to use the Transform methods of the Matrix3D and MatrixTransform3D classes. The resulting point has the following coordinates :

x’ = (M11x + M21y + M31z + OffsetX) / f
y’ = (M12x + M22y + M32z + OffsetY) / f
z’ = (M13x + M23y + M33z + OffsetZ) / f

where f = M14x + M24y + M34z + M44

The fourth column of the matrix (represented by the f factor), defines non-affine transformations, ie transformations that do not respect the parallelism of the sides of a rectangle, typically like a perspective view...

The identity matrix has a value 1 for M11, M22, M33 and M44, and 0 for the other cells. The f factor has the the value 1. The constructor of the Matrix3D structure creates by default an identity matrix.

A transformation can be applied to a vector according to the same principle as for a point.

Scaling

The M11, M22 and M33 properties determine the scale factor, respectively on the X, Y and Z axis, as shown by the equation :

x’ = M11x 
y’ = M22y
z’ = M33z

The Helper3D Class of Perspective 2.0 library provides the following static method which returns a scaling matrix :

public static Matrix3D GetScaleMatrix(double scaleX, double scaleY, double scaleZ)
{
    return new Matrix3D
    {
        M11 = scaleX,
        M22 = scaleY,
        M33 = scaleZ
    };
}

Under WPF, this method could also be written as follows :

public static Matrix3D GetScaleMatrix(double scaleX, double scaleY, double scaleZ)
{
    var m = new Matrix3D();
    m.Scale(new Vector3D(scaleX, scaleY, scaleZ));
    return m;
}

Translation

A translation is defined by properties OffsetX, OffsetY and OffsetZ :

public static Matrix3D GetTranslationMatrix(double offsetX, double offsetY, double offsetZ)
{
    return new Matrix3D
    {
        OffsetX = offsetX,
        OffsetY = offsetY,
        OffsetZ = offsetZ
    };
}

This matrix corresponds to the following equation :

x’ = x + OffsetX
y’ = y + OffsetY
z’ = z + OffsetZ

Under WPF, this method could also be written as follows :

public static Matrix3D GetTranslationMatrix(double offsetX, double offsetY, double offsetZ)
{
    var m = new Matrix3D();
    m.Translate(new Vector3D(offsetX, offsetY, offsetZ));
    return m;
}

Rotation

For rotation, things get a little complicated, and involve trigonometry.

In 2D, the rotation of a point at an angle beta gives the following equation, as demonstrated in my previous article Les matrices 2D de WPF et Silverlight :

x' = x * cos(beta) - y * sin(beta)
y' = y * cos(beta) + x * sin(beta)

In 3D, the équation becomes :

x' = x * cos(beta) - y * sin(beta)
y' = y * cos(beta) + x * sin(beta)
z' = z

So, the 3D matrix corresponding to this equation is :

M11 = cos(beta)M21 = -sin(beta)M31 = 0M41 = 0
M12 = sin(beta)M22 = cos(beta)M32 = 0M42 = 0
M13 = 0M23 = 0M33 = 1M43 = 0
OffsetX=0OffsetY=0OffsetZ=0M44=1

The following method returns a Matrix3D object defining a rotation around the z-axis at an angle expressed in degrees :

public static Matrix3D GetZRotationMatrix(double angle)
{
    var radianAngle = GeometryHelper.DegreeToRadian(angle);
    return new Matrix3D
    {
        M11 = Math.Cos(radianAngle), M12 = Math.Sin(radianAngle),
        M21 = -Math.Sin(radianAngle), M22 = Math.Cos(radianAngle)
    };
}

This method is used as follows :

Point3D p3 = new Point3D(1.0, 0.0, 0.0);
p3 *= Helper3D.GetZRotationMatrix(initialAngle);
DisplayPoint(p3, zWorkshop, grayMaterial);
p3 *= Helper3D.GetZRotationMatrix(angle);
DisplayPoint(p3, zWorkshop);

Z rotation

The code of this example is available in the Matrix3DDemo.xaml page of the Perspective 2.0 for WPF library example application.

By the same principle, a rotation around the x-axis is defined by the following equation:

x' = x
y' = y * cos(beta) - z * sin(beta)
z' = z * cos(beta) + y * sin(beta) 

Then the 3D matrix corresponding to the rotation of a point around the X axis (in the YZ plane) is :

M11 = 1M21 = 0M31 = 0M41 = 0
M12 = 0M22 = cos(beta)M32 = -sin(beta)M42 = 0
M13 = 0M23 = sin(beta)M33 = cos(beta)M43 = 0
OffsetX=0OffsetY=0OffsetZ=0M44=1

The following method returns a Matrix3D object defining a rotation around the x-axis at an angle expressed in degrees :

public static Matrix3D GetXRotationMatrix(double angle)
{
    var radianAngle = GeometryHelper.DegreeToRadian(angle);
    return new Matrix3D
    {
        M22 = Math.Cos(radianAngle), M32 = -Math.Sin(radianAngle),
        M23 = Math.Sin(radianAngle), M33 = Math.Cos(radianAngle)
    };
}

X rotation

Finally, a rotation around the y-axis is defined by the following equation:

x' = x * cos(2 * PI - beta) - z * sin(2 * PI - beta)
y' = y
z' = z * cos(2 * PI - beta) + x * sin(2 * PI - beta) 

Because WPF is a right-handed 3D system, a positive angle value must result in a counter-clockwise rotation around the axis. In respect to the relative orientation of Z to X, the beta angle is inverted (2 * PI - beta).

The 3D matrix corresponding to the rotation of a point around the y-axis (in the XZ plane) is then :

M11 = cos(2 * PI - beta)M21 = 0M31 = -sin(2 * PI - beta)M41 = 0
M12 = 0M22 = 1M32 = 0M42 = 0
M13 = sin(2 * PI - beta)M23 = 0M33 = cos(2 * PI - beta)M43 = 0
OffsetX=0OffsetY=0OffsetZ=0M44=1

The following method returns a Matrix3D object defining a rotation around the y-axis at an angle expressed in degrees :

public static Matrix3D GetYRotationMatrix(double angle)
{
    var radianAngle = (2 * Math.PI) - GeometryHelper.DegreeToRadian(angle);
    return new Matrix3D
    {
        M11 = Math.Cos(radianAngle), M31 = -Math.Sin(radianAngle),
        M13 = Math.Sin(radianAngle), M33 = Math.Cos(radianAngle)
    };
}

Rotation Y

The rotations presented above are suitable for a right-handed coordinate system (WPF). In a left-handed one, the direction of rotation must be reversed. The Perspective library (for WPF and Silverlight) automatically adjusts the rotation type to the handedness of the coordinate system.

Matrix multiplication

Several matrices (each representing a transformation) may be combined by multiplying them together. Matrix multiplication is not commutative (m1 * m2 does not give the same result as m1 * m2).

Matrix m1 = Helper3D.GetZRotationMatrix(45.0);
Matrix m2 = Helper3D.GetTranslationMatrix(50.0, 0.0, 0.0);
_currentPoint *= m1 * m2;

The following example applies 2 rotations to a BOX3D cube by multiplying 2 matrices together and pass them to the constructor of a MatrixTransform3D object :

Matrix3D mXRotation = Helper3D.GetXRotationMatrix(45.0);
Matrix3D mZRotation = Helper3D.GetZRotationMatrix(45.0);
MatrixTransform3D mt = new MatrixTransform3D(mXRotation * mZRotation);
box.Transform = mt;

Matrices multiplication

3D spaces

Basically, to represent a 3D model on screen, several spaces should be successively implemented : the model space, the world, the view, the projection and the screen viewport. Each space has its own coordinate system, and the transition from one system to another is done by applying a 3D matrix.

The model space is the one in which the 3D model is defined ; its origin is usually at the center of the model, or at the first point of the structure.

The world, or universe, is the 3D space of the application in which the model evolves ; the coordinates of the model are defined by the World matrix, which is generally derived from a combination of translation, rotation and scaling matrices.

The view is a transformation of the universe so that it seems to be seen by the point of view of a virtual camera ; this transformation is defined using the View matrix, which determines the position and orientation of the camera.

The projection defines the 2D representation of the 3D scene, using the Projection matrix.

Finally, the screen matrix screen (or Viewport matrix) defines the dimensions and position of the scene in the 2D container.

Each of these matrices may be derived from the combination of several matrices.

While Direct3D uses matrices to define those various spaces, WPF offers an higher level approach and automatically handles view and projection using the Camera object of the Viewport3D control. The World matrix is determined by the transformations applied to the model.

The open-source 3D engine Balder for Silverlight also encapsulates the view and projection in a camera object, but allows to define the position in the world through a matrix assigned in the World property of the 3D model.

In its version 4, Silverlight does not integrate natively a classic 3D engine but can represent a 2D element (UIElement) in perspective. It's actually a 3D projection made using a PlaneProjection object referenced in the element's Projection property. PlaneProjection is a high-level class, easy to use. But the UIElement.Projection property also accepts an object of type Matrix3DProjection, which allows to define a projection using matrices. Thanks to matrices, it is possible to reproduce the features of PlaneProjection, but above all to enhance it, for example by making the field of view of the virtual camera adjustable.

Silverlight 2D Elements perspective with Matrix3DProjection

An object Matrix3DProjection defines a transformation using a matrix of type Matrix3d referenced in its ProjectionMatrix property. The Matrix3DProjection object is then assigned to the Projection property of the Silverlight element.

private void ApplyPerspectiveProjection(FrameworkElement element, ...)
{
    var m = new Matrix3D();
    // matrix definition
    // ...
    Matrix3DProjection m3dProjection = new Matrix3DProjection();
    m3dProjection.ProjectionMatrix = m;
    element.Projection = m3dProjection;
}

The other parameters of this method will be described later

The unit matrix created by the constructor is multiplied by successive matrices defining world space, view, and projection screen. The matrices are provided by the methods of the Perspective library outlined above.

The code of this example is available in the Matrix3DDemo.xaml page of the Perspective 2.0 for Silverlight library example application.

World

A first matrix is used to determine the central point of rotations using the parameters centerXOfRotation, centerYOfRotation and centerZOfRotation (equivalent to CenterOfRotationX, CenterOfRotationY and CenterOfRotationZ properties of the PlaneProjection class). centerXOfRotation and centerYOfRotation values have a range of 0-1 and are 0.5 by default.

double width = element.ActualWidth;
double height = element.ActualHeight;
m *= Helper3D.GetTranslationMatrix(
        -width * centerXOfRotation,
        -height * centerYOfRotation,
        -centerZOfRotation);

CenterOfRotationX, CenterOfRotationY and CenterOfRotationZ properties of the class PlaneProjection are respectively unrelated with RotationX, RotationY and RotationZ properties. Therefore, a different terminology has been adopted for the parameters centerXOfRotation, centerYOfRotation and centerZOfRotation.

A second matrix can be applied using the parameters localOffsetX, localOffsetY and localOffsetZ (equivalent to the corresponding properties of Planeprojection). This translates the element along its respective axis after it has been rotated.

m *= Helper3D.GetTranslationMatrix(
    localOffsetX,
    localOffsetY,
    localOffsetZ);

A new matrix reflects the reversal of the y-axis between 2D and 3D.

m *= Helper3D.GetScaleMatrix(1.0, -1.0, 1.0);

Rotations can then be applied according to the angles defined in degrees through the rotationX, rotationY and rotationX parameters (equivalent to the corresponding properties of Planeprojection).

if (rotationX != 0.0)
{
    m *= Helper3D.GetXRotationMatrix(rotationX);
}
if (rotationY != 0.0)
{
    m *= Helper3D.GetYRotationMatrix(rotationY);
}
if (rotationZ != 0.0)
{
    m *= Helper3D.GetZRotationMatrix(rotationZ);
}

A translation can be applied after the rotation, using the parameters globalOffsetX, globalOffsetY and globalOffsetZ (equivalent to the corresponding properties of Planeprojection). This translation occurs in the global coordinate system (ie the screen).

m *= Helper3D.GetTranslationMatrix(
    globalOffsetX,
    globalOffsetY,
    globalOffsetZ);

View

The view matrix defines the position of a virtual camera whose vertical field of view is specified in the variable fovY. We can consider that the camera has a coordinate system whose Z axis is reversed. A negative translation is defined on the Z axis in order to present the Silverlight element in its actual size using the appropriate trigonometric formula.

m *= Helper3D.GetTranslationMatrix(0.0, 0.0,
    -height / Math.Tan(GeometryHelper.DegreeToRadian(fovY) / 2.0));

Projection

The GetPerspectiveMatrix method Of the Perspective for Silverlight Library's Helper3D Class defines a perspective projection similar to that of the WPF's PerspectiveCamera class. Here, compared to PlaneProjection, the matrix system allows us to vary the field of view (set at 57 degrees for PlaneProjection, according !link!"Jaime Rodriguez"! http://blogs.msdn. com/b/jaimer/archive/2009/06/03/silverlight3-planeprojection-primer.aspx ), and the clipping zone (set at 999 for PlaneProjection according the same source), or we could also set in example an orthographic projection.

m *= Helper3D.GetPerspectiveMatrix(
    fovY,
    width / height,   // format d'image
    1.0,              // avant-plan
    1000.0);          // arrière-plan

Data are lacking, however, to reproduce exactly the rendering of Planeprojection.

Screen matrix (viewport)

Finally, a Viewport matrix restores the orientation of the Y axis for 2D, and cancels the original translation of the rotation point.

m *= new Matrix3D
{
    M11 = width,
    M22 = -height,
    OffsetX = width * centerXOfRotation,
    OffsetY = height * centerYOfRotation
};

Here is the rendering of a rotation of 30 degrees around the y-axis :

ApplyPerspectiveProjection(image10, centerXOfRotation:1.0, rotationY:30.0);

Y30 Rotation

Downloads

Perspective 2.0 for WPF

Perspective 2.0 for Silverlight

About this article

Author : Olivier Dewit

History :

  • The 24th august 2010 : creation