Screen to World coordinates

Hi,

I am using an ortho camera and trying to convert from screen to world coordinates using this calculation. But it does not seem to work.

Can anyone explain why?

Previously i was, getting the ratio of screen pos to screen size, multiplying that ratio by the ortho camera frustum size, then adding that to the left and top of the frustum.

I thought i would change to the way below so it would work for all camera types, not just ortho.

	ci::mat4 matProjection = m_pCameraOrtho->getProjectionMatrix() * m_pCameraOrtho->getViewMatrix();
	ci::mat4 matInverse = ci::inverse(matProjection);

	float x = vPos.x;
	float y = vPos.y;

	float in[4];
	float winZ = 1.0;

	//  convert to NDC normalized device coordinates
	in[0] = (2.0f * ((float)(x - 0) / (ci::app::getWindowWidth() - 0))) - 1.0f,
	in[1] = 1.0f - (2.0f * ((float)(y - 0) / (ci::app::getWindowHeight() - 0)));
	in[2] = 2.0 * winZ - 1.0;
	in[3] = 1.0;

	ci::vec4 vIn = ci::vec4(in[0], in[1], in[2], in[3]);

	//  convert to world coordiantes
	ci::vec4 _vPos = vIn * matInverse;

	_vPos.w = 1.0 / _vPos.w;

	//  scale up to pixel coordinates
	_vPos.x *= _vPos.w;
	_vPos.y *= _vPos.w;
	_vPos.z *= _vPos.w;

	return ci::vec2(_vPos);

Hi,

to convert from window coordinates (I’ll use the mouse here) to world coordinates, you can simply use the glm::unProject() function:

ivec2 mouse = ...; // obtain mouse coordinates in the mouseMove() function.

mat4 projection = cam.getProjectionMatrix() * cam.getViewMatrix();

int w = getWindowWidth();
int h = getWindowHeight();
vec4 viewport = vec4( 0, h, w, -h ); // vertical flip is required

vec3 worldCoordinate = 
    glm::unProject( vec3( mouse, 0 ), mat4(), projection, viewport );

When drawing your content, you should store the model matrix. This way, you can later also retrieve local coordinates:

mat4 model =  glm::rotate( glm::radians( 30.0f ), vec3( 0, 0, 1 ) );

gl::pushModelMatrix();
gl::setModelMatrix( model );
gl::drawStrokedRect( Rectf( 0, 0, 100, 100 ) );
gl::popModelMatrix();

vec3 localCoordinate = 
    glm::unProject( vec3( mouse, 0 ), model, projection, viewport );

if( Rectf( 0, 0, 100, 100 ).contains( vec2( localCoordinate ) ) )
    CI_LOG_I( "Rectangle touched!" );

-Paul

P.S.: if you’re going to use glm::unProject a lot, it will be faster to call it once per frame to obtain a worldCoordinate (use mat4() for the second parameter), then for each object on screen multiply it by its inverse model matrix to obtain the local coordinate:
vec4 localCoordinate = glm::inverse( model ) * vec4( worldCoordinate, 1 ).

(Post taken from the old forums).

1 Like

Once again, Paul thank you :slight_smile: Will be book marking this as its something i always keep coming back to.