Cant translate world coords to screen

Started by
5 comments, last by ankhd 8 years, 6 months ago

Hello. I am creatin own engine from scratch in d3d9. Lookin on forum and google i found the way to create a ray and check if it intersects given area. It works. But i cannot do it the other way. To feed a D3DXVECTOR3 and retrieve XY on the screen. I found several "solutions" on the internet but damn they give wrong results.

So from what i've read i have to:

1) create matrix from projection * view

2) use the new matrix on given D3DXVECTOR3 containing world location

3) the screen x will be (vector.x + 1) * 0.5 * width

But something is clearly wrong because i see the output values. Is it wrong alghoritm or should i show my code?

Thanks in advance

Greetz

Advertisement
View times projection, not projection times view.

The result will be NDC, so -1 to 1 across both axes.

Don't really understand how this relates to a ray though. If you are doing mouse picking, you need the opposite - a screen position converted into a world ray.

Thanks for such a quick reply. I am tryin to do basic RTS game so i will obviously need to select units (so create a ray to check if it selects some object under cursor) but i will also need to draw HP bar for example over unit's head which requires the 3d unit location to be converted to screen, to draw the 2D bar just above.

So to achieve first i created a white box with edges on (-60, -13, -60), (60, -10, 60) and this piece of code detects perfectly if the ray hit (the code is called every frame when i move cusor so i see if the "ray hit" text disappears or shows up). "s" is mouse xy:


        D3DVIEWPORT9 vp;
	d3ddev->GetViewport(&vp);

	D3DXMATRIX view;
	D3DXMATRIX proj;
	d3ddev->GetTransform(D3DTS_VIEW, &view);
	d3ddev->GetTransform(D3DTS_PROJECTION, &proj);

	float px = (((2.0f * s.x) / vp.Width) - 1.0f) / proj(0, 0);
	float py = (((-2.0f * s.y) / vp.Height) + 1.0f) / proj(1, 1);

	Ray ray;
	ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	ray._direction = D3DXVECTOR3(px, py, 1.0f);
		
	D3DXMATRIX viewInverse;
	D3DXMatrixInverse(&viewInverse, 0, &(view));
	D3DXVec3TransformCoord(&ray._origin, &ray._origin, &viewInverse);
	D3DXVec3TransformNormal(&ray._direction, &ray._direction, &viewInverse);

	D3DXVec3Normalize(&ray._direction, &ray._direction);

	if (D3DXBoxBoundProbe(&D3DXVECTOR3(-60, -13, -60), &D3DXVECTOR3(60, -10, 60), &ray._origin, &ray._direction))
	{
		RECT rect = { 20, 400, 220, 450 };
		font.printf(rect, D3DCOLOR_XRGB(123, 123, 123), "RAY HIT");
	}

I assume it is enough to loop through all objects and check if D3DXBoxBoundProbe returns true with their collision box and this way i will select unit (is it the optimal way tho?).

I understand its all about multiplying matrices, but I have to admit that i dont fully understand few d3d things. If we are in 3D world, why are matrices 4x4 and not 3x3? Lookin at functions "D3DXVec3TransformNormal" and "D3DXVec3TransformCoord" i see they differ only by "w" value 0/1. What is the magical "w" (msdn doesnt help sad.png )?

Greetz

Edit: Yeah the matrix multiplication order was messed in 3d to screen :) I read also on the xyzw, i get everything now :) Thanks a lot

How about this? offset adjusts the world position forward or backward relative to the camera and is probably not necessary for what you need.


float4 WorldToScreen(const float4& v, 
    float offset,
    float4x4 &orientation,
    float4x4 &modelView,
    float4x4 &projection,
    float width, float height, float _near, float _far)
{
    
    float4 result = modelView * (orientation * v);
    result.z -= offset;
    result = projection * result;



    // Homogeneous divide
    const double rhw = 1 / result.w;

    return float4(
        (1 + result.x * rhw) * width / 2,
        (1 + result.y * rhw) * height / 2,  // or (1 - result.y * rhw) * height / 2,
        (result.z * rhw) * (_far - _near) + _near,
        rhw);
}
Re w, it w is zero, translations are effectively ignored when multiplying by a 4x4 matrix so convention is to use 1 for points and 0 for directional vectors like normals

There is probably more to this that others can expand upon, but use the Coord form for positions and the Normal form for normals etc.

We use 4x4 and extend 3D vectors to 4 with w so we can encode translation and scaling into the same matrix as rotation. This is normally described as homogenous I think.
The way to avoid looping through all the boxes would be to use some kind of spatial partitioning (broadphase) to identify the objects that might be intersected by your ray. You can then do your bounding box check on a smaller subset. This is a whole topic in itself though and, depending on the number of units involved, your approach may be fine. It just doesn't scale very well.

You might want to look into something I'm currently doing. I'm using Bullet 3D physics library but just using sections of it (broadphase, GJK etc) rather than doing a physics sim. This stuff is hard to get right yourself. Bullet is nice and modular in this respect.

So, for free, you can use the Bullet dynamic tree to give you a broadphase, use the Bullet shapes to more closely fit your units and the Bullet ray cast to get your result. Be far more robust than anything you write yourself.
Hey there.

Use d3dx project with identity matrix done

This topic is closed to new replies.

Advertisement