Prometheus3k
Prometheus3k

Reputation: 153

World space to screen space (perspective projection)

I'm using a 3d engine and need to translate between 3d world space and 2d screen space using perspective projection, so I can place 2d text labels on items in 3d space. I've seen a few posts of various answers to this problem but they seem to use components I don't have.

I have a Camera object, and can only set it's current position and lookat position, it cannot roll. The camera is moving along a path and certain target object may appear in it's view then disappear. I have only the following values

Can anyone please give me an algorithm that will do this using just these components?

Many thanks.

Upvotes: 4

Views: 7893

Answers (3)

iforce2d
iforce2d

Reputation: 8272

Assuming you know the orientation of your camera in world space, with unit vectors for the camera frame forward/right/up directions, you can:

  • find where the line between the camera and your target object intersects the near plane
  • find how far away that point is from the near plane center
  • use that distance as a fraction of the near plane size, to know what fraction of screen space your label should be away from screen center

You'll need to know the world size of the near plane:

#define DEGTORAD 0.01745329252

float aspectRatio = w / (float)h; // window/viewport dimensions

float nearPlaneHeight = 2 * tan(fovY * 0.5f * DEGTORAD) * nearPlaneDist;
float nearPlaneWidth = nearPlaneHeight * aspectRatio;

This function returns true if the given world point is in front of the near plane:

bool getScreenPos(vec3 worldPos, vec2& screenPos) {

    vec3 nearPlaneCenter = camera.location + nearPlaneDist * camera.forward;

    float dot1 = dot(camera.forward, camera.location);
    float dot2 = dot(camera.forward, nearPlaneCenter);
    float dot3 = dot(camera.forward, worldPos);

    if ( (dot3 < dot2 && dot1 < dot2) || (dot3 > dot2 && dot1 > dot2)) {
        return false; // camera and target are on same side of near plane
    }

    // distance from camera to near plane, as a fraction of distance from camera to target
    float fraction = (dot2 - dot1) / (dot3 - dot1);

    vec3 cameraToTarget = worldPos - camera.location;
    vec3 pointOnNearPlane = camera.location + fractionToNearPlane * cameraToTarget;

    // this is a line on the near plane, from its center to where the ray intersects
    vec3 projOnNearPlane = pointOnNearPlane - nearPlaneCenter;

    // these are the component-wise magnitudes of that line in the camera frame
    float rightDot = dot(projOnNearPlane, camera.right);
    float upDot =    dot(projOnNearPlane, camera.up);

    screenPos.x = w/2 + w * rightDot / nearPlaneWidth;
    screenPos.y = h/2 - h * upDot    / nearPlaneHeight;

    return true;
}

Upvotes: 0

tamat
tamat

Reputation: 316

all graphics engines use matrices to transform between different coordinats systems. Indeed OpenGL and DirectX uses them, because they are the standard way.

Cameras usually construct the matrices using the parameters you have:

  • view matrix (transform the world to position in a way you look at it from the camera position), it uses lookat position and camera position (also the up vector which usually is 0,1,0)

  • projection matrix (transforms from 3D coordinates to 2D Coordinates), it uses the fov, near, far and aspect.

You could find information of how to construct the matrices in internet searching for the opengl functions that create them:

  • gluLookat creates a viewmatrix

  • gluPerspective: creates the projection matrix

But I cant imagine an engine that doesnt allow you to get these matrices, because I can ensure you they are somewhere, the engine is using it.

Once you have those matrices, you multiply them, to get the viewprojeciton matrix. This matrix transform from World coordinates to Screen Coordinates. So just multiply the matrix with the position you want to know (in vector 4 format, being the 4º component 1.0).

But wait, the result will be in homogeneous coordinates, you need to divide X,Y,Z of the resulting vector by W, and then you have the position in Normalized screen coordinates (0 means the center, 1 means right, -1 means left, etc).

From here it is easy to transform multiplying by width and height.

I have some slides explaining all this here: https://docs.google.com/presentation/d/13crrSCPonJcxAjGaS5HJOat3MpE0lmEtqxeVr4tVLDs/present?slide=id.i0

Good luck :)

P.S: when you work with 3D it is really important to understand the three matrices (model, view and projection), otherwise you will stumble every time.

Upvotes: 9

LarsH
LarsH

Reputation: 28004

so I can place 2d text labels on items in 3d space

Have you looked up "billboard" techniques? Sometimes just knowing the right term to search under is all you need. This refers to polygons (typically rectangles) that always face the camera, regardless of camera position or orientation.

Upvotes: 1

Related Questions