Setting up our scenes Eye and Projection Matrix with Direct3D

Back to Direct3D Tutorial Index

Welcome to the third installment to my Direct 3D tutorials. Here I will be showing
you have to set up the projection matrix and eye point for your scene. The projection
matrix will allow for objects closer the the eye point to be larger than those which
are further back in the scene.

-------------------------------------------------------------------------------------
Section 1: Headers, Libraries, Global Variables

Along with our standard d3d9.h header file, we now need to include d3dx9.h which
allows for many more d3d related functions. So after you add the line:
#include <dx/d3dx9.h>
You should now be referencing to 2 header files.

Likewise with the library file. You should have 2 listed after you add:
#pragma comment(lib,"d3dx9.lib")

Now variable wise, we need to edit our CUSTOMVERTEX struct. So that it now
takes only x, y, z and color components as we are no longer working with
homogeneous coordinates.

-------------------------------------------------------------------------------------
Section 2: Initializing

For our scene to display our square in projection mode, we need to disable face culling
and disable lighting so that we may specify our own primitive colors.
The next 2 lines will take care of that. These are place in our init function after our
call to CreateDevice.
g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

-------------------------------------------------------------------------------------
Section 3: Initializing the Vertex Buffer

In our initVBuffer function, I am changing only are vertices. The things I am
changing are:
I am removing the rhw value.
The unit of measurement has change, so we are no longer working with pixels.

Because we are working with units of a practically unmeasureable nature, eg pixels
texels, metres, centimetres, inches, etc. We just measure our distances in units.

**NOTE**
All units are centred around the coordinate (0,0,0) which will be situated in the middle
of the scene. A positive y value is up, while a positive x value is to the right.
Therefore a negative y value will be down, and a negative x value will be to the left.
As for the Z values, a negative will come out to the screen (unless a y axis rotation has occured)
and a positive will move further into the scene. The Z axis basically goes from front to back,
front being the negative value.
********

So here I am using a distance of 1 on either side of both the x and y axis. This will
create a distance of 2 units from the top of the square and the bottom. Likewise for
the left and the right. If you run this, you will probably notice that our square will
take up most of our scene. At a distance of 2 from an object of 2 units in with and height
will fit perfectly on the screen. I find that for each unit you move away from an object
you gain another 2 units to both your width and height.
So a distance of 1 will give you a distance of 2 units from top to bottom and left to
right, while a distance of 10 will give you 20 units from left to right and top to
bottom.

CUSTOMVERTEX Square[] =
{
{ -1, 1, 0, D3DCOLOR_XRGB(255,0,0), },
{ -1, -1, 0, D3DCOLOR_XRGB(0,0,255), },

{ 1, 1, 0, D3DCOLOR_XRGB(255,255,0), },
{ 1, -1, 0, 0xff00ff00, },
};

---------------------------------------------------------------------------------------
Section 4: The Eye

In 3D programming, there is no such thing as a camera or an eye. So a method of
setting how you view the scene is given to us by the creators. This is called
the LookAt command and is common in both OpenGL and Direct 3D. It takes
the x, y and z coordinates of where the 'eye' is to be placed, the x, y and z coordinates
of where you wish the camera to look at, and the x, y and z values of the Up Vector.

I am calling these inside the function which I have called Eye.

void Eye (void)
{

This first call will set up a Direct 3D vector to hold our Eye position, I have situated
this at 0, 0, -5, which will bring the camera back 5 units.
D3DXVECTOR3 EyePoint( 0, 0,-5 );
Next I am setting up another Direct 3D vector, this one will set where we are looking at
which I have set to the location 0, 0, 0.
D3DXVECTOR3 LookatPoint( 0, 0, 0 );
The last of these 3 is the Up Vector. I have set this up with another Direct 3D vector
but this one is rather tricky, having it with the values 0, 1, 0 will display the scene
correctly, changing the second value to -1 will flip your screen vertically. Keep an
eye on these values as they could distort the way your scene is viewed if inputted wrong.
D3DXVECTOR3 UpVector( 0, 1, 0 );

Next I am setting up a Direct 3D matrix called mView, which will transform our
VIEW of the scene.
D3DXMATRIXA16 mView;
Now this call, will set up our view. It will take the Eye position, Lookat Point and
our Up Vector, and place them all together.
D3DXMatrixLookAtLH( &mView, &EyePoint, &LookatPoint, &UpVector );
Finally we tell our rendering device to set our transforming matrix to our view matrix
and apply our above choices.
g_pd3dDevice->SetTransform( D3DTS_VIEW, &mView );
}

-----------------------------------------------------------------------------------------
Section 5: Perspective

This function here will set up our windows Projection view port.
It will set our Projection matrix, our FOV (field of view), our aspect ratio and
our near and far factors.
void Perspective (void)
{

To start this function off, I need to assign a matrix which will hold our projection
data. This will be called mProjection.
D3DXMATRIXA16 mProjection;
I am then calling the command:
D3DXMatrixPerspectiveFovLH( &mProjection, D3DX_PI/4, 1, 0.1, 100 );
Which is a left handed perspective matrix. The first value is our projection matrix,
the second is our FOV or Field of View which is usually in degrees. A nice field of view
ranges from 45 to 80. The next value is our apect ratio. 1 means it is a 1 to 1 ratio.
For every 1 unit across, there is one up. This is often measured as the width of the
window divided by the height for the best look, but that requires some API that does not
relate to Direct 3D, so I might add it in a later tutorial. The last 2 values are the near
and far planes. Anything drawn prior to the near plane is cut from view, and anything drawn
after the far plane is also cut from the scene.

Then I just need to tell our rendering device to transform our projection matrix.
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &mProjection );
}

------------------------------------------------------------------------------------------
Section 6: Render

We have already set everything up that we need so now all we have to do is call our functions.

So inside our Render function, place the following lines after our call to BeginScene.

Eye();
Perspective();

------------------------------------------------------------------------------------------
And there we have it. We now have a projective view which will show objects smaller which are
further away from the 'eye'.

If you have any questions about this tutorial, please feel free to email me at swiftless@gmail.com

#include <dx/d3d9.h>
#include <dx/d3dx9.h>

#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")

LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;

struct CUSTOMVERTEX
{
float x, y, z;
DWORD color;
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

void init( HWND hWnd )
{

g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice );

g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

}

void initVBuffer (void)
{
CUSTOMVERTEX Square[] =
{
{ -1, 1, 0, D3DCOLOR_XRGB(255,0,0), },
{ -1, -1, 0, D3DCOLOR_XRGB(0,0,255), },

{ 1, 1, 0, D3DCOLOR_XRGB(255,255,0), },
{ 1, -1, 0, 0xff00ff00, },
};

g_pd3dDevice->CreateVertexBuffer( 4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL );

void* pVertices;
g_pVB->Lock( 0, sizeof(Square), (void**)&pVertices, 0 );

memcpy( pVertices, Square, sizeof(Square) );

g_pVB->Unlock();
}

void Eye (void)
{
D3DXVECTOR3 EyePoint( 0, 0,-5 );
D3DXVECTOR3 LookatPoint( 0, 0, 0 );
D3DXVECTOR3 UpVector( 0, 1, 0 );

D3DXMATRIXA16 mView;
D3DXMatrixLookAtLH( &mView, &EyePoint, &LookatPoint, &UpVector );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &mView );
}

void Perspective (void)
{
D3DXMATRIXA16 mProjection;
D3DXMatrixPerspectiveFovLH( &mProjection, D3DX_PI/4, 1, 0.1, 100 );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &mProjection );
}

void Cleanup (void)
{

if( g_pVB != NULL )
{
g_pVB->Release();
}

if( g_pd3dDevice != NULL)
{
g_pd3dDevice->Release();
}

if( g_pD3D != NULL)
{
g_pD3D->Release();
}
}

void Render (void)
{
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1, 0 );

g_pd3dDevice->BeginScene();

Eye();

Perspective();

g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );

g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );

g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

g_pd3dDevice->EndScene();

g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;

case WM_PAINT:
Render();
ValidateRect( hWnd, NULL );
return 0;
}

return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, "A Basic Window", NULL };
RegisterClassEx( &wc );

HWND hWnd = CreateWindow( "A Basic Window", "A Basic Window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, wc.hInstance, NULL );

init( hWnd );

initVBuffer();

ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );

MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}

UnregisterClass( "A Basic Window", wc.hInstance );
return 0;
}

Download C++ Source Code for this Tutorial

     

 

Copyright 2008, Donald Urquhart
Proudly supported by http://www.cdadc.com