PDA

View Full Version : How to flatten view?


ikk
10-12-2005, 01:45 AM
How can i flatten view so 3d world looks like it would be a 2d.
That is, i want to get rid of perspective in the way that it looks like in every 3d object on scene one of vector components (ie. x) is zero.
I know i can modify every vertex by writing zero into x-component, but how can i achieve this by matrix transformations?

moe
10-12-2005, 01:58 AM
For rendering on screen you have:
world * view * projection
Now you have to replace the projection matrix with an orthogonal matrix.
You can find further info here:
http://mathworld.wolfram.com/OrthogonalMatrix.html
or here:
http://encyclopedia.thefreedictionary.com/orthogonal+matrix

ikk
10-12-2005, 04:57 AM
ok, thanks

ikk
10-12-2005, 05:11 AM
im currently working on it. i appreciate any suggestions

moe
10-12-2005, 05:59 AM
I was not more specific for two reasons.
- I’m not that good at math
- when I try to explain math I tend to make things more complicates :(

If you are using directx you can just use the provided functions instead of reinventing the wheel.

From the docu:

D3DXMATRIX *WINAPI D3DXMatrixOrthoLH(
D3DXMATRIX *pOut,
FLOAT w,
FLOAT h,
FLOAT zn,
FLOAT zf
);

Parameters:
pOut [in, out] Pointer to the D3DXMATRIX structure that contains the resulting matrix.
w [in] Width of the view volume.
h [in] Height of the view volume.
zn [in] Minimum z-value of the view volume which is referred to as z-near.
zf [in] Maximum z-value of the view volume which is referred to as z-far.


And then set it as usual.
pd3dDevice->SetTransform( D3DTS_PROJECTION, & pOut);

hope it helps.

ikk
10-12-2005, 08:24 AM
Im using function u mentioned (D3DXMatrixOrthoLH) but there is another problem.
Collision testing against rays doesnt work now.

I have this code to produce from screen points rays into world space, it works when i setup projection using D3DXMatrixPerspectiveFovLH function but not when D3DXMatrixOrthoLH.

here is function i took from directx sample, as ia said, it converts screen points into world rays (origin and direction), and that rays are propably incorrect now.

maybe someone know what do i need update in my functon.

VOID CDevice3d::
GetScreen2DPointToWorld3DPoint( const POINT* lpScreenPoint, D3DXVECTOR3* lpvOrigin,
D3DXVECTOR3* lpvDirection )
{
POINT ptScreenSize;
GetScreenSize( &ptScreenSize );
D3DXVECTOR3 v; D3DXMATRIX matProj;

pd3dDeviceC->GetTransform( D3DTS_PROJECTION, &matProj );
v.x = ( ( ( 2.0f * lpScreenPoint->x ) / ptScreenSize.x ) - 1 ) / matProj._11;
v.y = -( ( ( 2.0f * lpScreenPoint->y ) / ptScreenSize.y ) - 1 ) / matProj._22;
v.z = 1.0f;
// get the inverse of view matrix
D3DXMATRIX matViewInv, m;
pd3dDeviceC->GetTransform( D3DTS_VIEW, &m );
D3DXMatrixInverse( &matViewInv, NULL, &m );

// Transform the screen space pick ray into 3D space
{
// get ray direction vector
if(lpvDirection){
lpvDirection->x = v.x * matViewInv._11 + v.y * matViewInv._21 + v.z * matViewInv._31;
lpvDirection->y = v.x * matViewInv._12 + v.y * matViewInv._22 + v.z * matViewInv._32;
lpvDirection->z = v.x * matViewInv._13 + v.y * matViewInv._23 + v.z * matViewInv._33;
}
// get ray origin (start point), which is actually same as camera pos
if(lpvOrigin){
lpvOrigin->x = matViewInv._41;
lpvOrigin->y = matViewInv._42;
lpvOrigin->z = matViewInv._43;
}
}
}

moe
10-12-2005, 09:06 AM
In your comment you write

// get ray origin (start point), which is actually same as camera pos

Think about how the view frustum looks like. If you have a normal projection then the frustum is like a pyramid. So yes all the rays go through the camera pos.

But if you have an orthogonal matrix then the frustum is more like a stretched cube (don’t know the appropriate word in English…). This means the rays are parallel to one another and only the ray in the middle of your frustum goes through the camera pos.

ikk
10-12-2005, 01:37 PM
still i dont know how to do it :(

moe
10-12-2005, 02:06 PM
Let’s make a sample.

D3DXMATRIX *pOut;
FLOAT w = 640;
FLOAT h = 480;
FLOAT zn = 1.0f;
FLOAT zf = 100.0f;

D3DXMatrixOrthoLH( &pOut, w, h, , zn, zf );


Assuming your camera is at origin (identity matrix) then your direction would be

D3DXVECTOR3 vDir = D3DXVECTOR3( 0.0f, 0.0f ,1.0f );

You pick a point on screen e.g.

D3DXVECTOR3 vPointOnScreen;
vPointOnScreen.x = 20.0f;
vPointOnScreen.y = 100.0f;
vPointOnScreen.z = 0.0f; // this is always 0.0f

get world coordinates from point on screen:

D3DXVECTOR3 vPointInWorld;
vPointInWorld.x = -((w / 2.0f) – vPointOnScreen.x);
vPointInWorld.y = (h / 2.0f) – vPointOnScreen.y;
vPointInWorld.z = 0.0f; // this is always 0.0f

now take the view matrix into account:

D3DXVec3TransformCoord( &vPointInWorld, &vPointInWorld, &matView );
D3DXVec3TransformCoord( &vDir, &vDir, &matView );

Did that make more sense?


p.s. I have not tested it but the strategy should be right.

moe
10-13-2005, 01:43 AM
Darn that’s just a sample of what I mean with I tend to make things more complicated when trying to explain math. Your way of finding the direction is correct. In my sample above I take the translation from the view matrix into account. You should only take the rotation. However finding the start point (origin) should be correct. In that case you take translation and rotation from the view matrix. Sorry for confusing you.

ikk
10-13-2005, 08:39 AM
ok thanx, i think ill leave this problem for now

ikk
11-07-2005, 02:48 AM
ive used yours advices and i solved this problem mow.
im using this code to create orthogonal view:

// DESC: makes world looks like flattern
VOID CCamera::MakeOrthogonal( DWORD dwOrthogonalPr )
{
lpvPosSaves[dwViewMode] = GetOrigin(); //save previous mode pos
dwViewMode = dwOrthogonalPr; //set new camera mode

D3DXMATRIX m;
// build left-handed orthogonal projection matrix
D3DXMatrixOrthoLH( &m,
fUnitsPerWidth, //width of view volume in world units
fUnitsPerHeight, //height of view volume in world units
fZNearClipPlane, //z-near
MAX_WORLD_Z * 2 ); //z-far, fZFarClipPlane
lpD3dDev->SetTransform( D3DTS_PROJECTION, &m );
//
D3DXVECTOR3 vEye, vAt, vUp;
DefaultEyeAtSetup( dwViewMode, &vEye, &vAt, &vUp );
D3DXMatrixLookAtLH( &m, &vEye, &vAt, &vUp );
lpD3dDev->SetTransform( D3DTS_VIEW, &m );

SetPlacement( &lpvPosSaves[dwViewMode], NULL );
}

and this code to get ray of mouse pick point:

// DESC: converts on screen point into point and direction in 3d world space.
// lpScreenPoint - [in] x,y coordinates of screen point relative to upper left corner of
// client area
// lpvOrigin, lpvDirection - [out] two vectors, origin and direction. note: origin is
// in world space and direction is isloated/individual one (jednostkowy), that is
// it points direction of origin vector
VOID CCamera::
GetScreen2DPointAsWorldRay( const POINT* lpScreenPoint, D3DXVECTOR3* lpvOrigin,
D3DXVECTOR3* lpvDirection )
{
POINT ptScreenSize;
((CDevice3d*)lpcDevice)->GetScreenSize( &ptScreenSize );
FLOAT x = lpScreenPoint->x;
FLOAT y = lpScreenPoint->y;
FLOAT sx = ptScreenSize.x;
FLOAT sy = ptScreenSize.y;

D3DXMATRIX matProj;
lpD3dDev->GetTransform( D3DTS_PROJECTION, &matProj );

// get the inverse of view matrix
D3DXMATRIX mi;
lpD3dDev->GetTransform( D3DTS_VIEW, &mi );
D3DXMatrixInverse( &mi, NULL, &mi );

// calculate how many world units is per screen width and height.
// from 'D3DXMatrixOrthoLH' function documentation its known that
// matProj._11 = w/2 and matProj._22 = h/2.
FLOAT fUnitsPerWidh = 2.0f / matProj._11;
FLOAT fUnitsPerHeight = 2.0f / matProj._22;
// calculate position on screen in world units (WU)
FLOAT fXOnScrWU = x * fUnitsPerWidh / sx;
FLOAT fYOnScrWU = y * fUnitsPerHeight / sy;
// take into account that center of the screen for world is (0,0), not
// left/upper (like in Win32 GUI)
FLOAT fXWrldWU = fXOnScrWU - fUnitsPerWidh / 2.0f; //division by 2 bcos needed are coordinates from center of screen
FLOAT fYWrldWU = -( fYOnScrWU - fUnitsPerHeight / 2.0f ); //minus, bcos in GUI y is from top to bottom but in world from bottom to top

D3DXVECTOR3 vRayDir, vRayOrig;
if( dwViewMode != CCAM_PERSP ){ // if orthogonal view
vRayDir = D3DXVECTOR3( 0.0f, 0.0f, 1.0f );
vRayOrig = D3DXVECTOR3( fXWrldWU, fYWrldWU, 0.0f ); //for orthogonal viw (without perspective)
}else{
vRayDir = D3DXVECTOR3( fXWrldWU, fYWrldWU, 1.0f );
vRayOrig = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); //for perspective view

}
D3DXVec3TransformNormal( lpvDirection, &vRayDir, &mi );
D3DXVec3TransformCoord( lpvOrigin, &vRayOrig, &mi );
}