🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

World coordinates to screen coordinates

Started by
12 comments, last by taby 3 years, 8 months ago

I'm calling a function:

// Get pixel coordinates
vertex_3 p = get_screen_coords_from_world_coords(transformed_vertex, camera_pos, projection_modelview_mat, win_x, win_y);

// Detect region
GLint y_viewport_pos = -1;

if (p.y < 0)
	y_viewport_pos = ABOVE_VIEWPORT;
else if (p.y < win_y)
	y_viewport_pos = IN_VIEWPORT;
else
	y_viewport_pos = BELOW_VIEWPORT;

GLint x_viewport_pos = -1;

if (p.x < 0)
	x_viewport_pos = LEFT_OF_VIEWPORT;
else if (p.x < win_x)
	x_viewport_pos = IN_VIEWPORT;
else
	x_viewport_pos = RIGHT_OF_VIEWPORT;

where

vertex_3 get_screen_coords_from_world_coords(const vertex_3 p, const vertex_3 c, const float(&mat)[16], const int width, const int height)
{
    vertex_3 v = p - c;
    v.x /= v.z;
    v.y /= v.z;
    v.z /= v.z;

    float point[4];
    point[0] = c.x + v.x;
    point[1] = c.y + v.y;
    point[2] = c.z + v.z;
    point[3] = 0;

    float out[4];

    multiply_4x4_matrix_by_4_vector(mat, point, out);

    return  vertex_3(
        round(((1.0f + out[0]) * 0.5f) * (float)width),
        round(((1.0f - out[1]) * 0.5f) * (float)height), 
        0.0f);
}

This function tells the 2D pixel coordinate of a 3D vertex. It works great for 8 of 9 cases, except for the last case, This last case is where the x location lies on screen and the y location lies below the screen. Instead of detecting these last case locations, it detects that the location is actually above the screen – that is, the value of p.y < 0, where it should be p.y > win_y. Any idea why? Thanks for your time.

P.S.


void init_perspective_camera(float fovy, float aspect, float znear, float zfar,
                             float eyex, float eyey, float eyez, float centrex, float centrey,
                             float centrez, float upx, float upy, float upz,
                             float (&projection_modelview_mat)[16])
{
    float projection_mat[16];
    get_perspective_matrix(fovy, aspect, znear, zfar, projection_mat);
    
    float modelview_mat[16];
    get_look_at_matrix(eyex, eyey, eyez, // Eye position.
                       centrex, centrey, centrez, // Look at position (not direction).
                       upx, upy, upz,  // Up direction vector.
                       modelview_mat);
    
    multiply_4x4_matrices(projection_mat, modelview_mat, projection_modelview_mat);
}

and

void multiply_4x4_matrix_by_4_vector(const float(&in_a)[16], const float(&in_b)[4], float(&out)[4])
{
    float temp[4];

    for (int i = 0; i < 4; i++)
    {
        temp[i] = 0.0;

        for (int j = 0; j < 4; j++)
        {
            temp[i] += in_a[i*4 + j] * in_b[j];
        }
    }

    for (size_t i = 0; i < 4; i++)
        out[i] = temp[i];
}

void multiply_4x4_matrices(float (&in_a)[16], float (&in_b)[16], float (&out)[16])
{
    /*
    matrix layout:
    
    [0 4  8 12]
    [1 5  9 13]
    [2 6 10 14]
    [3 7 11 15]
    */
    
    out[0] =  in_a[0] * in_b[0] +  in_a[4] * in_b[1] +  in_a[8] *  in_b[2] +  in_a[12] * in_b[3];
    out[1] =  in_a[1] * in_b[0] +  in_a[5] * in_b[1] +  in_a[9] *  in_b[2] +  in_a[13] * in_b[3];
    out[2] =  in_a[2] * in_b[0] +  in_a[6] * in_b[1] +  in_a[10] * in_b[2] +  in_a[14] * in_b[3];
    out[3] =  in_a[3] * in_b[0] +  in_a[7] * in_b[1] +  in_a[11] * in_b[2] +  in_a[15] * in_b[3];
    out[4] =  in_a[0] * in_b[4] +  in_a[4] * in_b[5] +  in_a[8] *  in_b[6] +  in_a[12] * in_b[7];
    out[5] =  in_a[1] * in_b[4] +  in_a[5] * in_b[5] +  in_a[9] *  in_b[6] +  in_a[13] * in_b[7];
    out[6] =  in_a[2] * in_b[4] +  in_a[6] * in_b[5] +  in_a[10] * in_b[6] +  in_a[14] * in_b[7];
    out[7] =  in_a[3] * in_b[4] +  in_a[7] * in_b[5] +  in_a[11] * in_b[6] +  in_a[15] * in_b[7];
    out[8] =  in_a[0] * in_b[8] +  in_a[4] * in_b[9] +  in_a[8] *  in_b[10] + in_a[12] * in_b[11];
    out[9] =  in_a[1] * in_b[8] +  in_a[5] * in_b[9] +  in_a[9] *  in_b[10] + in_a[13] * in_b[11];
    out[10] = in_a[2] * in_b[8] +  in_a[6] * in_b[9] +  in_a[10] * in_b[10] + in_a[14] * in_b[11];
    out[11] = in_a[3] * in_b[8] +  in_a[7] * in_b[9] +  in_a[11] * in_b[10] + in_a[15] * in_b[11];
    out[12] = in_a[0] * in_b[12] + in_a[4] * in_b[13] + in_a[8] *  in_b[14] + in_a[12] * in_b[15];
    out[13] = in_a[1] * in_b[12] + in_a[5] * in_b[13] + in_a[9] *  in_b[14] + in_a[13] * in_b[15];
    out[14] = in_a[2] * in_b[12] + in_a[6] * in_b[13] + in_a[10] * in_b[14] + in_a[14] * in_b[15];
    out[15] = in_a[3] * in_b[12] + in_a[7] * in_b[13] + in_a[11] * in_b[14] + in_a[15] * in_b[15];
}
Advertisement

First of all you should really consider obtaining 3 matrices that define how point is transformed then you could try obtaining the coord, yes this one is of of the most common used over internet so maybe someone with more knowledge on this could tell you exactly whats going on. Besides vec4 spos = (model*view)*projection;

Then divide xyz by w value then multiple by 0.5 + 0.5 then youll get screen coord

@_WeirdCat_ I will alter the code so that it multiplies by view before projection. Thanks for the tip.

It didn't work. :(

taby said:
Instead of detecting these last case locations, it detects that the location is actually above the screen – that is, the value of p.y < 0, where it should be p.y > win_y. Any idea why?

If 8 out of 9 work well otherwise, all your math should be right, i guess. So maybe the test data is misleading for some reason. Could it be all your failing test points are behind the camera, or something like that?

JoeJ said:

taby said:
Instead of detecting these last case locations, it detects that the location is actually above the screen – that is, the value of p.y < 0, where it should be p.y > win_y. Any idea why?

If 8 out of 9 work well otherwise, all your math should be right, i guess. So maybe the test data is misleading for some reason. Could it be all your failing test points are behind the camera, or something like that?

Hmm. That's a great idea. Let me find out.

@joej It is true that the problematic points are behind the camera. Thanks for pointing out that possibility. I'm not sure how to proceed though – I've tried a few things, but nothing seems to work right.

vertex_3 v1 = camera_pos - look_at_pos;
vertex_3 v2 = camera_pos - transformed_vertex;

if (v1.dot(v2) <= 0)
{
	//if (y_viewport_pos == ABOVE_VIEWPORT)
	//	y_viewport_pos = BELOW_VIEWPORT;
	
	//SDL_ShowSimpleMessageBox(MB_OK, "Test", "behind camera", gWindow);
}

if point reside behind camera its z value is below 0,

Actually, I just figured it out.

vertex_3 v1 = camera_pos - look_at_pos;
vertex_3 v2 = camera_pos - transformed_vertex;

bool behind_camera = false;

if (v1.dot(v2) <= 0)
	behind_camera = true;

if (y_viewport_pos == BELOW_VIEWPORT && x_viewport_pos == LEFT_OF_VIEWPORT)
{
	arrow a;
	a.opengl_init(arrow_down_left_image);
	a.draw(ortho.get_program(), static_cast<size_t>(0), static_cast<size_t>(win_y), win_x, win_y);
}
else if (y_viewport_pos == BELOW_VIEWPORT && x_viewport_pos == RIGHT_OF_VIEWPORT)
{
	arrow a;
	a.opengl_init(arrow_down_right_image);
	a.draw(ortho.get_program(), static_cast<size_t>(win_x - 64), static_cast<size_t>(win_y), win_x, win_y);
}
else if (y_viewport_pos == IN_VIEWPORT && x_viewport_pos == LEFT_OF_VIEWPORT)
{
	arrow a;
	a.opengl_init(arrow_left_image);
	a.draw(ortho.get_program(), 0, win_y / 2 - 64 / 2, win_x, win_y);
}
else if (y_viewport_pos == IN_VIEWPORT && x_viewport_pos == RIGHT_OF_VIEWPORT)
{
	arrow a;
	a.opengl_init(arrow_right_image);
	a.draw(ortho.get_program(), win_x - 64, win_y / 2 - 64/2, win_x, win_y);
}
else if (y_viewport_pos == IN_VIEWPORT && x_viewport_pos == IN_VIEWPORT)
{
	i->draw(ortho.get_program(), static_cast<GLint>(p.x), static_cast<GLint>(p.y), win_x, win_y);
}
else // all that's left is above and below viewport, where x location is in viewport
{
	if (behind_camera)
	{
		arrow a;
		a.opengl_init(arrow_down_image);
		a.draw(ortho.get_program(), win_x / 2 - 64 / 2, win_y, win_x, win_y);
	}
	//else
	//{
	//	arrow a;
	//	a.opengl_init(arrow_up_image);
	//	a.draw(ortho.get_program(), win_x / 2 - 64 / 2, win_y, win_x, win_y);
	//}
}

taby said:
Actually, I just figured it out.

… only to realize later there are still special cases where it fails, at least if z becomes zero, i guess. Looks like a hack : )

What do you try to do? Show some kind of GUI for stuff that may be behind the camera? Do you really need to support the behind front clip plane case?

This topic is closed to new replies.

Advertisement