World Space Coordinates of Screen Fragments 2: Manchester Prep




    float sinPhi = sinf(camZRot);
    float cosPhi = cosf(camZRot);
    float sinTheta = sinf(camXRot);
    float cosTheta = cosf(camXRot);
    float xInFrontOfCamera = (1.0 * sinPhi * sinTheta);
    float yInFrontOfCamera = (1.0 * cosPhi * sinTheta);
    float zInFrontOfCamera = (1.0 * cosTheta);

Therefore, the central point of the camera frustum’s near plane in world space is:

    float centreOfNearCameraPlaneWSX = -camXPos - xInFrontOfCamera * MAIN_FRAMEBUFFER_NEAR_PLANE;
    float centreOfNearCameraPlaneWSY = -camYPos - yInFrontOfCamera * MAIN_FRAMEBUFFER_NEAR_PLANE;
    float centreOfNearCameraPlaneWSZ = -camZPos - zInFrontOfCamera * MAIN_FRAMEBUFFER_NEAR_PLANE;

We need to figure out where the bottom-left hand corner of the near plane of the camera frustum is in world space, because that will map to (0, 0) in screen coordinates. To do that we need to know the dimensions of the near camera plane in world space units, and the vectors along which the camera plane’s x and y axes are aligned. Fortunately, these are exactly the same as “up” and “right” from the camera’s point of view. “Up” is conveniently always going to be found by rotating pi/2 radians anticlockwise around the x-axis, so the unit “up” vector is:

    float sinThetaUp = sinf(camXRot - piOverTwo);
    float cosThetaUp = cosf(camXRot - piOverTwo);
    float cameraUpX = (1.0 * sinPhi * sinThetaUp);
    float cameraUpY = (1.0 * cosPhi * sinThetaUp);
    float cameraUpZ = (1.0 * cosThetaUp);

The “right” vector is now easy to get: it’s the cross-product of the camera facing vector and the camera “up” vector. Now we can work out where the bottom left-hand corner of the clipping plane is:

    float aspectRatio = screenWidth/screenHeight;
    float nearClippingPlaneHeight = (2 * tanf(CAMERA_FOV / 2) * NEAR_PLANE_DISTANCE);
    float nearClippingPlaneWidth = nearClippingPlaneHeight * aspectRatio;
    float bottomLeftX = centreOfNearCameraPlaneWSX - (cameraUpX * halfNearClippingPlaneHeight) - (cameraRightX * halfNearClippingPlaneWidth);
    float bottomLeftY = centreOfNearCameraPlaneWSY - (cameraUpY * halfNearClippingPlaneHeight) - (cameraRightY * halfNearClippingPlaneWidth);
    float bottomLeftZ = centreOfNearCameraPlaneWSZ - (cameraUpZ * halfNearClippingPlaneHeight) - (cameraRightZ * halfNearClippingPlaneWidth);

We pass the world space position of the bottom left corner, screen dimensions, “up” vector and “right” vector to our shader as uniforms, and we can get the world space position of screen fragments in a single line, like so:

    vec3 worldSpacePositionOfScreenFragment = vec3(bottomLeftCornerWS + cameraUpVector*nearPlaneDimensionsWS.y * (gl_FragCoord.y/screenHeightInPixels) + cameraRightVector * nearPlaneDimensionsWS.x * (gl_FragCoord.x/screenWidthInPixels);

The final result feels less precise than the previous technique, but might be a bit cheaper in terms of shader calculations.

Leave a Reply
