# Direct2D Math Routines for Perspective Projection • ### Question

• Hi --

I'd like to be able to locate/project 3D x, y, z points onto my Direct2D surface.

I think this is the roll-your-own way:
http://en.wikipedia.org/wiki/3D_projection#Perspective_projection but I wonder if there are DirectX API calls I can do to obtain this?

I'd like to have:
-my camera at <0, 0, 0>, oriented at 0 degrees
-pass a point <x, y, z>
-pass the viewer's position relative to the display surface.

Result: get back a 2D x, y for where I should render my point.

Once I can do this for points then I can draw lines and polygons in my 3D space and project onto a 2D surface.

I understand that Direct3D would make sense for this but I want to see if I can do this on my Direct2D surface.

Thank you!

p.s. if my thinking above is just plain the wrong way to approach this please let me know (hopefully with a steer in the right direction :)

• Edited by Friday, July 13, 2012 8:54 PM
Friday, July 13, 2012 8:53 PM

### All replies

• Hi --

I found some functions in DirectXMath Library that gave what I wanted:

1. XMMatrixRotationX(), XMMatrixRotationY(), XMMatrixRotationZ() (to create the matrices for the camera rotation)
2. and XMVector4Transform().

Below is MyRenderer::Render() from a star field sample (a ship flies through a star-field along the +z axis and and its camera can rotate to change its view -- like looking out a window in a moving vehicle) that I made showing how I project the points using the mathematics described in the Wikipedia article.

Any comments on this method (especially if there are any better ways to do this) would be appreciated!

```void MyRenderer::Render()
{
/*
See Wilikpedia, Perspective projection: http://en.wikipedia.org/wiki/3d_projection
The below code implements those equations and methodology to map our 3D points (stars)
to our 2D screen based on the ship's position and the camera rotation.
*/

//---------------------------------------------------------
//Locals

D2D1_POINT_2F
p0 = D2D1_POINT_2F(),
p1 = D2D1_POINT_2F();

//---------------------------------------------------------
//Begin Draw

m_d2dContext->BeginDraw();

//---------------------------------------------------------
//Initial transform

//Is this needed?
m_d2dContext->SetTransform(D2D1::Matrix3x2F::Identity());

//---------------------------------------------------------
//Initial clear

m_d2dContext->Clear(D2D1::ColorF(D2D1::ColorF::Black));

//---------------------------------------------------------
//Transform to center

D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(m_screenCenter);
m_d2dContext->SetTransform( trans );

//---------------------------------------------------------
//Rotation matrices

XMMATRIX mrX = XMMatrixRotationX( m_angleX );
XMMATRIX mrY = XMMatrixRotationY( m_angleY );

//---------------------------------------------------------
//Camera and the viewer's position relative to the display surface

XMVECTOR c = { 0.f, 0.f, m_z, 0.f };
XMVECTOR e = { 0.f, 0.f, 1.f, 0.f };

//---------------------------------------------------------
//Calculate the field of view
m_fov = 2.f * atan(1.f / e.m128_f32) * (180.f / XM_PI);

//---------------------------------------------------------
//Stars

for (list<CStar *>::iterator it = m_StarObjects.begin(); it != m_StarObjects.end(); it++) {

XMVECTOR a = (*it)->_p;
XMVECTOR aMinusC = a - c;
XMVECTOR d = XMVector4Transform(aMinusC, mrX * mrY);

XMMATRIX hc = XMMATRIX(
1.f, 0.f, 0.f, -e.m128_f32,
0.f, 1.f, 0.f, -e.m128_f32,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 1.f/e.m128_f32, 0.f
);

XMVECTOR f = XMVector4Transform(d, hc);

m_bx = f.m128_f32 / f.m128_f32;
m_by = f.m128_f32 / f.m128_f32;

//Scale to screen coords.
//Note we do not do an aspect ratio, we just let the 1:1 of our original box
//map to the screen assuming pixels are equally wide as high.
m_bx = ( m_bx * m_size.width / 2.f );
m_by = -( m_by * m_size.height / 2.f ); //notice, negate y to coord screen to real world

//Point (as a 1 pixel sized rectangle since it seems Direct2D has no pixel draw).
if ( (*it)->_t.m128_f32 > 0.f ) {
m_d2dContext->DrawRectangle(
D2D1::RectF( m_bx-0.5f, m_by-0.5f, m_bx+0.5f, m_by+0.5f ), m_whiteBrush.Get(), 1.f );
}

//Save transformed so we can check when a Star is no longer viable.
(*it)->_t = d;
}

//Remove Stars that are no longer able to be visualized at any angle (outside our +-planeBoundary cube area).
//Note: you have to obtain the next iterator value before erasing the element the iterator is pointing to.
list<CStar *>::iterator it = m_StarObjects.begin();
float planeBoundary = 2.f * m_createPlaneDeltaZ;
while( it != m_StarObjects.end() ) {
if (

//x
( (*it)->_t.m128_f32 < -planeBoundary )	||
( (*it)->_t.m128_f32 > planeBoundary )	||

//y
( (*it)->_t.m128_f32 < -planeBoundary )	||
( (*it)->_t.m128_f32 > planeBoundary )	||

//z
( (*it)->_t.m128_f32 < -planeBoundary )	||
( (*it)->_t.m128_f32 > planeBoundary )
) {
it = m_StarObjects.erase(it);
} else {
it++;
}
}

#if (0)
//---------------------------------------------------------
//Debug Text
m_d2dContext->SetTransform(D2D1::Matrix3x2F::Identity());
Platform::String^ text =
"Field of View: " + m_fov + " Degrees\n" +
"Stars in List: " + m_StarObjects.size();
m_d2dContext->DrawText(
text->Data(),
text->Length(),
m_textFormat.Get(),
D2D1::RectF(10.0, 0.0, m_renderTargetSize.Width, m_renderTargetSize.Height),
m_whiteBrush.Get()
);
#endif

//---------------------------------------------------------
//Reset Initial transform

//Is this needed?
m_d2dContext->SetTransform(D2D1::Matrix3x2F::Identity());

//---------------------------------------------------------
//End Draw

HRESULT hr = m_d2dContext->EndDraw();

//---------------------------------------------------------

if (hr == D2DERR_RECREATE_TARGET)
{
m_d2dContext->SetTarget(nullptr);
m_d2dTargetBitmap = nullptr;
CreateWindowSizeDependentResources();
}
else
{
DX::ThrowIfFailed(hr);
}
}```

• Edited by Thursday, July 26, 2012 6:24 PM
Thursday, July 26, 2012 6:22 PM