Model Space

Introduction

What is Model Space?

View Matrix

The Projection Matrix

World Matrix

Viewport

Piecing the Transforms Together

Perspective

Lighting

Lost Devices

Wrapping Up

Lesson Downloads

Introduction

So far we've only used Transformed (Screen Space) vertices. Transformed vertices have a number of limitations:

  • To move them, you have to lock the Vertex Buffer and re-write the position values
  • Lighting (which we haven't covered yet) doesn't affect Transformed vertices.
  • No perspective. Objects with small Z values are the same size as similar objects with large Z values.
Transformed vertices still have their place, but most often you will use Untransformed vertices defined in Model Space.

What is Model Space?

As mentioned in the 3D Primer, Model Space vertices are defined relative to each other, often with (0,0,0) in the center. When we used Screen Space coordinates it was obvious what each unit was equal to, 1 pixel. In Model Space you can define your units to be whatever size you want them to be. They can be inches, feet, meters or light-years, whatever scale suits your needs. Because most games are done in a human-relative scale, 1 meter is a very common unit. Also, while it's not required it's a good idea to make your units consistent. If 1 unit is 1 meter for 1 model, make it the same for each other model as well as your World Space units. If your Model Space units and World Space units are different then you'll have to scale them when they are rendered.

To render using Untransformed vertices 3 matrices need to be set up: the World Matrix, the Projection Matrix and the View Matrix. Additionally, there is the Viewport which is used to build a 4th matrix internally. First I'll show each is built, then I'll explain how they work together.

View Matrix

The View Matrix is our camera. We have to define its position and its orientation. The position is simply a 3-element vector of floats that represent it's x,y,z position in World Space. This vector is often called the Eye Vector. The orientation is given by 2 vectors, the Look At Vector and the Up Vector.

The Look At Vector is a 3-element vector that defines the point that the camera is looking at. This is used to calculate the viewing angle and the actual distance between the Eye and Look At points doesn't matter.

The up vector points, well, up. Typically this will be set to (0,1,0) which points along the Y-axis. But if you wanted your camera (and thus your scene) to be upside down, then you could set it to (0,-1,0). Various other values will allow you to simulate banking and other effects.

The Matrix class has a static method called LookAtLH that we can use to create our View Matrix. Once we have our View Matrix built, we have to register it with our device. This is done by assigning our matrix to the Tansform.View property of our device.

   //This is the camera location and is commonly referred to as the "eye point"

   //Our camera is 8 units away from the origin along the Z-axis

   Vector3 eye_vector = new Vector3(0,0,-8);

   //The Look At Vector defines the point that the camera is aimed at, here we

   //set it to be the origin

   Vector3 lookat_vector = new Vector3(0,0,0);

   //The "up" direction is the positive direction on the y-axis

   Vector3 up_vector = new Vector3(0,1,0);

   //Register our View Matrix with our device so it can be used to place and

   //aim our camera

   m_device.Transform.View = Matrix.LookAtLH(eye_vector,
                                             lookat_vector,
                                             up_vector);
   'This is the camera location and is commonly referred to as the "eye point"

   'Our camera is 8 units away from the origin along the Z-axis

   Dim eyeVector As DX.Vector3 = New DX.Vector3(0, 0, -8)
   
   'The Look At Vector defines the point that the camera is aimed at, here we

   'set it to be the origin

   Dim lookatVector As DX.Vector3 = New DX.Vector3(0, 0, 0)
   
   'The "up" direction is the positive direction on the y-axis

   Dim upVector As DX.Vector3 = New DX.Vector3(0, 1, 0)
   
   'Register our View Matrix with our device so it can be used to place and

   'aim our camera

   _device.Transform.View = DX.Matrix.LookAtLH(eyeVector, lookatVector, upVector)

The Projection Matrix

If the View Matrix is our camera, the Projection Matrix would be the camera lens. To build a Projection Matrix we need the following:

Field of View
Defined in radians. For a standard camera, this is π/4 or 45 degrees.
Aspect Ratio
The Aspect ratio of the display. Width/Height.
ZNear
Near view plane, distance from the camera in World space units. Anything that is closer to the camera will be clipped (not drawn). This also defines the precision/scale of the Z buffer (more on that in a later lesson).
ZFar
Far view plane, distance from the camera in World space units. Anything farther will be clipped. This also affects the Z buffer along with the Near Plane.
   //45 degree field of view

   float field_of_view = (float)Math.PI / 4.0f;

   //Typical aspect ratio is approximately 1.333...

   float aspect_ratio = (float)ClientSize.Width / ClientSize.Height;

   m_device.Transform.Projection = Matrix.PerspectiveFovLH(field_of_view,
                                                           aspect_ratio,
                                                           1.0f,        //ZNear

                                                           100.0f);     //ZFar

   '45 degree field of view

   Dim fieldOfView As Single = CType(Math.PI / 4.0F, Single)
   
   'Typical aspect ratio is approximately 1.333...

   Dim aspectRatio As Single = _
       CType(Me.ClientSize.Width / Me.ClientSize.Height, Single)
   
   _device.Transform.Projection = DX.Matrix.PerspectiveFovLH( _
       fieldOfView, aspectRatio, 1.0F, 100.0F)  '1.0F=ZNear, 100.0F=ZFar

World Matrix

The World Matrix transforms your vertices from Model Space to World Space. The transforms that can occur include Translation (movement), Rotation, and Scaling. Each of these transforms is built using a separate matrix which is then combined to make your final World Matrix. Matrices are combined by multiplying them together. It's important to note that the order that you multiply the matrices is important. The order in which they are multiplied gives the order in which the operations are performed. Rotating then Translating will cause the model to spin on it's own axis, then get moved. Translating then Rotating will move the model and then cause it to revolve around it's origin.

In this lesson we don't want to move the mesh at all. To do this we set our World Matrix to the Identity matrix, which is a NOP or "Do Nothing" transform. In this case our Model Coordinates are our World Coordinates.

   m_device.Transform.World = Matrix.Identity;
   _device.Transform.World = DX.Matrix.Identity

Viewport

The Viewport defines the area of the screen that will be rendered to. By default it is set to the entire display, so we don't need to change it. The Viewport can also be used to scale Z values, but we won't touch on that here.

If we wanted to change the Viewport we'd initialize the structure and then assign it to the ViewPort property of the device. Though we build it as a structure, it gets converted into a matrix internally. If you needed to set our Viewport to the default, this is how you could do it.

   D3D.Viewport view_port = new D3D.Viewport();

   view_port.X = 0;
   view_port.Y = 0;
   view_port.Width = ClientSize.Width;
   view_port.Height = ClientSize.Height;
   view_port.MinZ = 0.0f;
   view_port.MaxZ = 1.0f;

   m_device.Viewport = view_port;
   Dim viewPort As D3D.Viewport = New D3D.Viewport
   
   With viewPort
     .X = 0
     .Y = 0
     .Width = ClientSize.Width
     .Height = ClientSize.Height
     .MinZ = 0.0F
     .MaxZ = 1.0F
   End With
   
   _device.Viewport = viewPort

Piecing the Transforms Together

To take your mesh from Model Space to Screen Space (where it can be rendered) the vertices will undergo 4 transformations. Here is a brief overview of the process.

The World Transform takes your Model Space vertices,which are usually centered on (0,0,0) and are defined in arbitrary units, and transforms them into World Space. World Space has the world centered on (0,0,0) and is also defined in arbitrary units. These units do not have to be the same as your Model Space units, but it makes things much easier.

Then the View Matrix is used to transform your World Space vertices into View (or Camera) Space. In View Space the viewer is at the origin (0,0,0) and is looking in the direction of the positive Z-axis.

After that the vertices are transformed into Projection Space using the Projection Matrix. In Projection space X and Y coordinates are scaled to be between -1 and 1, while the Z coordinates range from 0 to 1.

Finally, the viewport is used to transform the vertices into Screen Space where they can be rendered.

Perspective

In the introduction we mentioned that Screen Space coordinates lack perspective. In some cases that's a feature, other times it is not what you want. The Projection Matrix we supplied was created by a call to PerspectiveFovLH. As the name implies, this matrix is responsible for applying Perspective to our scene. If we wanted to use the flexibility of Untransformed Coordinates while not having Perspective we could create an Orthogonal Projection. We'll show how to do that in a later lesson.

Lighting

Lighting isn't covered in this lesson, but it has to be mentioned. By default Untransformed vertices are lit by Direct3D. Since we haven't added any lights, our scene would be rendered all black and we wouldn't see anything. To prevent out vertices being lit (by our 0 lights) we turn off lighting.

   m_device.RenderState.Lighting = false;
   _device.RenderState.Lighting = False

Lost Devices

As mentioned in previous lessons, there are resources and states that do not survive a device Reset. Two of these are Transforms and RenderStates. To handle this we initialize our matrices and set our RenderStates in the OnDeviceReset handler so they will be set up after a device Reset. Since neither one of these allocated resources there is still nothing to do in OnDeviceLost.

Wrapping Up

This early in the game it's not important that you memorize all the information we covered in this lesson. For the most part you'll simply set up your matrices and not worry about it. Still, it's nice to understand why and how things work. When you get to more advanced topics you will need to understand this stuff.

I didn't want to cover too much ground in one lesson, that's why we simply leave the World Matrix as Identity. In the next lesson we'll go into more detail with Translation, Rotation, and Scaling.

Lesson Downloads

Back