Direct2D Math Routines for Perspective Projection


  • 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 :)

    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.
    		p0 = D2D1_POINT_2F(), 
    		p1 = D2D1_POINT_2F();
    	//Begin Draw
    	//Initial transform
    	//Is this needed?
    	//Initial clear
    	//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[2]) * (180.f / XM_PI);
    	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);
    			1.f, 0.f, 0.f, -e.m128_f32[0],
    			0.f, 1.f, 0.f, -e.m128_f32[1],
    			0.f, 0.f, 1.f, 0.f,
    			0.f, 0.f, 1.f/e.m128_f32[2], 0.f
    		XMVECTOR f = XMVector4Transform(d, hc);
    		m_bx = f.m128_f32[0] / f.m128_f32[2];
    		m_by = f.m128_f32[1] / f.m128_f32[2];
    		//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[2] > 0.f ) {
    			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 ( 
    			( (*it)->_t.m128_f32[0] < -planeBoundary )	||
    			( (*it)->_t.m128_f32[0] > planeBoundary )	||
    			( (*it)->_t.m128_f32[1] < -planeBoundary )	||
    			( (*it)->_t.m128_f32[1] > planeBoundary )	||
    			( (*it)->_t.m128_f32[2] < -planeBoundary )	||
    			( (*it)->_t.m128_f32[2] > planeBoundary )
    		) {
    			it = m_StarObjects.erase(it);
    		} else { 
    #if (0)	
    	//Debug Text
    	Platform::String^ text = 
    		"Field of View: " + m_fov + " Degrees\n" + 
    		"Stars in List: " + m_StarObjects.size(); 
    		D2D1::RectF(10.0, 0.0, m_renderTargetSize.Width, m_renderTargetSize.Height),
    	//Reset Initial transform
    	//Is this needed?
    	//End Draw
    	HRESULT hr = m_d2dContext->EndDraw();
    	if (hr == D2DERR_RECREATE_TARGET)
    		m_d2dTargetBitmap = nullptr;

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