Basic directx transformation with VB .net
Introduction
In this tutorial we will try to discuss the basic transformation in
directX ,we will discuss only the world transform because it’s the simplest transform, but honestly I
should say it’s complicated subject , specially if you tried to understand the
underlying concepts of transformation then you will be lost in pure math problems.
So, I will not discuss the mathematic concepts of vectors and matrices (because I
don’t understand it myself!) but we learn how to use them for transformation.
Background
This tutorial is continuation of my previous tutorial “starting directx using visual basic” so I assume you already understand how to create a directx device.
Draw a square
“Download incomplete_project and start working on it”
Firstly we will draw a square before transforming it , declare this variable in
your class:
Dim buffer As VertexBuffer
In creat_vertxbuffer
Sub write:
Sub creat_vertxbuffer()
buffer = New VertexBuffer(GetType(CustomVertex.PositionColored), 4, device, _
Usage.None, CustomVertex.PositionColored.Format, Pool.Managed)
Dim ver(3) As CustomVertex.PositionColored
ver(0) = New CustomVertex.PositionColored(-0.5F, -0.5F, 0, Color.Red.ToArgb)
ver(1) = New CustomVertex.PositionColored(-0.5F, 0.5F, 0, Color.Green.ToArgb)
ver(2) = New CustomVertex.PositionColored(0.5F, -0.5F, 0, Color.Blue.ToArgb)
ver(3) = New CustomVertex.PositionColored(0.5F, 0.5F, 0, Color.Yellow.ToArgb)
buffer.SetData(ver, 0, LockFlags.None)
'the next line is not important it's for performance:
buffer.Unlock()
End Sub
Here we used
PositionColored
type in our vertices unlike the previous
tutorial where we used TransformedColored
type , because we want to apply transform on it,
also we used 4 vertices to draw a square.But the question is : according to what I determine the position of my vertices? I think you wondered about this position data. what this (0.5) and (-0.5) ? and you could say : in the previous tutorial the matter is clear we drew directly according to the screen coordinate ( 0,0 point in the upper left corner) , but that is exactly the difference !, we don’t draw according to screen coordinate , we draw according to Viewport coordinate.
Viewport
The view port is a rectangle where directx project (not draw) the
scenes, you can consider the view port as window open into scenes, see
below:
So the max X value in viewport is 1 and the
minimum is -1 the same as Y, but what about the 3rd dimension Z? Where is it?! And
what its min max values?.
To know the positive direction of Z axis in direct3d we
use left hand rule:
If your left hand point toward positive direction of X axis and
your fingers point toward positive direction of Y axis then the positive direction of Z
axis is the direction of your thump. As in picture:
And the min max value of Z is determined by you
:
Viewport.MinZ() as single
Viewport.MaxZ() as single
But the default values is MinZ
=0 and
MaxZ
=1 ,any value outside this range will be clipped (and this another subject!) but we
will ignore the Z axis completely in this tutorial for simplification purpose.
We can
set viewport for the device using:
device.Viewport = ourViewport
Moreover,
we can use more than one view port in our scenes; for now we will use the default view
port which in the size of screen.
Another important thing the viewport dimension
does not change with screen dimension or width height ratio of screen , mean that it
does not matter your form is oblong or square , and in any case the (-1,-1)point in the
lower left corner and (1,1) point in the upper right corner of your viewport.
The main loop
I think after above explanation you have imagined the position of the square, but you want to see not imagine! , OK, a few more code and you can see what we have done. Unlike previous tutorial we need here a main loop because we have to call drawing routine repeatly, so we put it all in a loop as here:
initilizdx()
creat_vertxbuffer()
Me.Show()
Do While Me.Created
transform()
draw_me()
Application.DoEvents()
Loop
'end the program immediatly to avoid any (null reference)error!
End
Application.DoEvents
is very important procedure in this infinite loop ,
it lets the program to process all system messages and return to the loop , if you did
not use it your program will not response to any message (closing for Example) .
Put the above code block in
Event. LoadForm
and write draw_me
Sub:
Sub draw_me()
device.Clear(ClearFlags.Target, Color.Black, 0, 0)
device.BeginScene()
device.VertexFormat = CustomVertex.PositionColored.Format
device.SetStreamSource(0, buffer, 0)
device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2)
device.EndScene()
device.Present()
End Sub
Slightly different from previous tutorial because we used
TriangleStrip
in primitive type , don’t worry I will explain , hit now F5 and see the result
.
A beautiful colorful square!
So what is
TriangleStripe
? The TriangleStripe
method form a triangle from the last three vertices, if you
want 2 triangles you should use 4 vertices as we did, and for 3 triangles use 5
vertices:
Triangle1(ver1,ver2,ver3),triangle2(ver2,ver3,ver4),triangle3
(ver3,ver4,ver5)…
When Ver = vertex
In other word the triangle use the last
side of previous triangle, resulting in shared side in any consecutive triangles.
In
better practice you should put a timer in your loop, but for now we write it without timer
to be simple.
But where is the transformation? We have learned how to draw
squares and triangles from previous tutorial and want now to move it around. Don’t
worry we will learn how to do so , but firstly you should know the primary tool of
transformation , Matrix.
Note: if you don’t interested in theories skip these
following sections and go ahead to (Practical)
section.
Matrix
The source of panic for any
beginner of graphics and 3D programming, and it’s really hard to understand. Most of
tutorials in internet doesn’t explain the concepts of matrix and (specially) matrix
multiplication , they says : this matrix, and this how to use it, and don’t ask any
questions!. And I will not be an exception!.
As we know matrix is a rectangular
layout of number in a form of rows and columns, an m x n matrix is a matrix have m
rows and n columns, the element of matrix named by the number of its row and the
number of its column, for example A2,3 means the element of A matrix In the second
row and 3rd column.
In above A matrix: A1,1 =2 , A3,1 =12 , A2,3 = 54 .
In 3D
transformation we use 4X4 matrix to represent the system coordinates and we call it
transformation matrix , if we want to transform our scenes we should multiply each
vertices by transformation matrix, also we can transform our matrix by another matrix
which result in cooperation effect.
In other word if you multiply matrix that rotate
around X axis by a matrix that rotate around Y axis the result is a matrix that rotate
around X axis then around y axis , if you invert the operation the result is a matrix that
rotate around Y axis then around X axis , because matrix multiplication is not
commutative.
But how matrix multiplication is don, in practice you don’t need to
know how to multiply matrix manually you can simply use (*) operator to do so (only in
managed directx) or you can use Matrix. Multiply ()
function which is a shared Sub in
Matrix
class , but if you resolved to understanding how to multiply matrix you can
search through the net or content by brief description below .
If A , B are
matrices then A X B is valid only if the number of columns in A = the number of rows
in B, the resulting matrix has number of rows of A and the number of columns of B.
The resulting matrix calculated by multiplication each row in A by each column in B
(better to say multiply matrix A by each column vector in B ) as in picture below:
As in the
picture the result of multiply row 1 by column 2 is a number and it stored in element1,2
in result matrix , guess which the element bear the result of row 3 * column 4?.
And here how we multiply row by column:
Didn’t understand? No problem as I said above
you don’t need to understand matrix multiplication because you can do it simply by *
operator!, A*B = result.
Returning to our subject, how to use this matrix in
transformation, firstly we should know how to represent the world coordinate in a matrix
This 4x4 matrix which used in transformation:
X oblong represent the vector of
X axis which normally is (1, 0, 0)
Y oblong represent the vector of y axis which
normally is (0, 1, 0)
Z oblong represent the vector of Z axis which normally is (0,
0, 1)
And G oblong represent the origin point which normally is (0, 0, 0)
The
W oblong doesn’t useful in world transformation.
If we applied these values in
an 4x4 matrix:
This matrix called
identity matrix because it’s identical to the coordinate system, means that any vertex or
matrix transformed by this matrix will not change.
You may wonder how we can
multiply 3 elements vector by this 4x4 matrix?!, if you want to multiply it manually you
have to use 4 elements vector (x,y,z,1), but if you used directx transformation(it’s the
best) you don’t need to worry about this.
Translation
The simplest transform in which we change the origin
point only:
As you see, to build a
translation matrix you only change the point of origin like this:
Replace (x, y, z) by your translation offset
values.
To do it by code, declare MatWorld
as Matrix
in your form class:
Dim MatWorld As Matrix
In sub transform
write this:
Sub transform()
' make MatWorld identity matrix:
MatWorld = Matrix.Identity
'set the number in row 4 , column 1:
MatWorld.M41 = 0.5
'set the number in row 4 , column 2:
MatWorld.M42 = 0.5
'assign MatWorld as world transformation matrix:
device.Transform.World = MatWorld
End Sub
Before hitting F5 can you imagine the result?, as you notice we
changed only the values we want (M41,M42) and leave the rest as the identity matrix .
the previous code build the following matrix:
1 0
0 0
0 1
0 0
0 0
1 0
0.5 0.5 0
1
And directx will transform each vertex by this world
Matrix.
Scaling
To build a scaling matrix you should determine firstly
which axises you want to scale , here we will scale both in y and x axises:
to do so by code, rewrite transform
sub as this:
Sub transform()
' make MatWorld identity matrix:
MatWorld = Matrix.Identity
'set the number in row 1 , column 1:
MatWorld.M11 = 0.5
'set the number in row 2 , column 2:
MatWorld.M22 = 0.5
'assign MatWorld as world transformation matrix:
device.Transform.World = MatWorld
End Sub
Because our square is big we scaled it to half, press
F5.
Rotation
Rotation is more complicated than previous transform
(specially in 3D), as I mentioned earlier we will ignore the Z axis , so our rotation
should be around Z axis to avoid any change in Z values. To build a rotation matrix
around Z axis by angle θ:
To do so by code, rewrite transform
sub as
following:
Sub transform()
' make MatWorld identity matrix:
MatWorld = Matrix.Identity
'set the number in row 1 , column 1 and column 2:
MatWorld.M11 = Math.Cos(Math.PI / 4) : MatWorld.M12 = Math.Sin(Math.PI / 4)
'set the number in row 2 , column 1 and column 2:
MatWorld.M21 = -Math.Sin(Math.PI / 4) : MatWorld.M22 = Math.Cos(Math.PI / 4)
'assign MatWorld as world transformation matrix:
device.Transform.World = MatWorld
End Sub
This code build a matrix that rotate around z axis by 45 degree, this is
the matrix we have built:
Cos(π/4) sin(π /4)
0 0
-sin(π/4) cos(π/4)
0 0
0 0
1 0
0
0 0
1
Practical
If you didn’t understand previous sections (or
didn’t read it at all) no problem, because we usually use matrix functions that comes
with direct3D to build transform matrices, here how to build a rotation matrix:
Sub transform()
' make MatWorld identity matrix:
MatWorld = Matrix.Identity
'make MatWorld as rotation matrix:
MatWorld.RotateZ(Math.PI / 4)
'or you can use the alternative :
' MatWorld = Matrix.RotationZ(Math.PI/4)
'assign MatWorld as world transformation matrix:
device.Transform.World = MatWorld
End Sub
Very easy!
You can use other matrix functions:
'for translation :
MatWorld.Translate()
'for scaling :
MatWorld.Scale()
'for rotation around other axises:
MatWorld.RotateX()
MatWorld.RotateY()
Q & A
Q: how I can rotate an object around its self away from origin
point?
A: best practice is to draw your objects in origin point rotate them and then
translate them to their positions, like this example:
'declare this in your form class:
Dim matrix2 As Matrix
Sub transform()
'make MatWorld as rotation matrix:
MatWorld.RotateZ(Math.PI / 4)
'make matrix2 as translation matrix:
matrix2.Translate(0.5, 0.5, 0)
'multiply MatWorld by matrix2:
MatWorld = MatWorld * matrix2
'assign MatWorld as world transformation matrix:
device.Transform.World = MatWorld
End Sub
Q: what will happen if I wrote matrix2 * MatWorld
instead?
A:
Matworld * matrix2
means rotate then translate, but matrix2* MatWorld
means translate
then rotate.
Q: I want my square keep rotation each frame , how I can do so?
A: you must keep rotating your matrix each frame!, like this:
'first make MatWorld identity matrix in the declaration:
Dim MatWorld As Matrix = Matrix.Identity
Sub transform()
'rotate MatWorld by a rotation matrix:
MatWorld = MatWorld * Matrix.RotationZ(0.01)
'make matrix2 as translation matrix:
matrix2.Translate(0.5, 0.5, 0)
device.Transform.World = MatWorld * matrix2
End Sub
Or you can change your angle each time.
Q: is it important to
make the matrix identity matrix before any operation?
A: No, but when you
declare a new matrix then its all elements are zero, if you multiply the new by any other
matrix the result is zero matrix also, so we make it identity before any operation, but if
you use Matrix.rotate
(for example) this function build a rotation matrix not rotate the
matrix, so you don’t need to make it identity before use this function.
Q: I want to
draw several objects and apply different transformation to each object, how i can do
so?
A: take this example:
First delete transform
sub , and apply
transformation inside draw_me
:
'declare these in form class
Dim matrix1 As Matrix = Matrix.Identity
Dim matrix2 As Matrix = Matrix.Identity
Dim matrix3 As Matrix = Matrix.Identity
Sub draw_me()
device.Clear(ClearFlags.Target, Color.Black, 0, 0)
device.BeginScene()
device.VertexFormat = CustomVertex.PositionColored.Format
device.SetStreamSource(0, buffer, 0)
'rotate matrix2 and matrix3 by different rotation matrices:
matrix2 = matrix2 * Matrix.RotationZ(0.01)
matrix3 = matrix3 * Matrix.RotationZ(-0.01)
'make matrix1 a translation matrix:
matrix1 = Matrix.Translation(0.3, 0.3, 0)
'rotate then translate:
device.Transform.World = matrix2 * matrix1
'draw the first square:
device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2)
'make matrix1 as translation matrix but in different direction this time:
matrix1.Translate(-0.3, -0.3, 0)
'rotate then translate:
device.Transform.World = matrix3 * matrix1
'draw the second square:
device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2)
device.EndScene()
device.Present()
End Sub
Q: why you ignored the 3rd dimension ? I can make all this work using
GDI!.
A: OK , I have said we ignored 3rd dimension for simplification purpose
but there is another reason, as you know there is no real 3D in directx because the
final picture which rendered in the screen is 2D , in order to simulate this 3D effect we
adjust the projection matrix
device.Transform.Projection
you should adjust this projection matrix to project the distal objects small and the proximal objects larg to simulate the real eye viewing to real world, but I think its enough for you to know the basics of transformation , and may in future I can explain to you the other types of transformation.
Conclusion
I hope I have written something
useful.
Sorry for my bad English!.
I usually use Google translate in writing
but I have no internet connection for several days ago so I used a mobile dictionary
which was horrible!.
The world transform is simplest transform and there is view
and projection transform which is more complicated and no beginner care to
understand them, if some people are interested I can write another tutorial to explain
how to use these transforms.