13

I have drawn diagram after diagram of how to calculate the bounding points of the viewing frustum in a three-dimensional space. To start, I have a two sets of data containing three values each: the xyz coordinates of the camera and the rotation around the x, y, and z axis. Given a certain view distance, it should be possible to calculate the bounding points of each of the 6 planes. I have been using these equations to calculate the width and height of the far plane:

hfar = 2 * tan(45/2) * view_distance
wfar = hfar * ratio

hfar being the height of the far plane, wfar being the width, and ratio being the ratio of the view port width divided by the height. I have been using the following diagram to try and figure it out:

enter image description here

I need to find the points annotated by (?,?,?). I have been trying to calculate these values for a few days now but to no avail. Any help would be appreciated.

Also, some nice sources providing information on the topic can be found here and here.

EDIT: Another image I whipped up shows a single slice through the y axis looking down on the x axis. It shows the same information as the image above, but it also shows my issue: I can't calculate the proper z axis values for each of the bounding points of the far plane.

enter image description here

Keep in mind, the same cut could be made through the x axis to show the same process but with the angle at which the player is looking up or down.

CoderTheTyler
  • 839
  • 2
  • 11
  • 30
  • You can use trig to figure this out. One piece of information I didn't see in your post was vertical FoV. You need this angle to figure out those points. Let A be (x,y,z), B be the center point of the far plane, and C be the midpoint of the upper edge of the far plane. These three points form a triangle with the angle closest to A being half of the vertical FoV. You can use tan() to figure out the half-height. The horizontal FoV can be determined from your ratio... and then you can figure out your half-width as well. – Prismatic Dec 02 '12 at 03:05
  • Considering my FOV is 45 degrees, I would take half of that, take the tangent of it, then multiply that by the view distance. This is done for the x and y axis. I understand that, but that procedure doesn't work for the z axis. – CoderTheTyler Dec 02 '12 at 03:08
  • Wow, how did I miss your formulas? Once you have the magnitudes for f_height/2 and f_width/2, their corresponding directions come from your camera's orientation vectors [let's call them up, side and front]. Is this what you're having trouble finding? – Prismatic Dec 02 '12 at 03:17
  • Exactly! My entire issue comes with being able to rotate the entire figure through space pointing at any point along the bounding sphere. But if you could explain that, I would be a very happy programmer :D – CoderTheTyler Dec 02 '12 at 03:18
  • With axis-angle rotations, you could apply your rotations to your initial up, side and front vectors using something like (http://paulbourke.net/geometry/rotate/). For example, your 'up' vector is initially [0,1,0]. Applying a rotation about an axis to that point will give you your new up vector. You do the same thing with your initial side and front vectors... and then you can use the initial method you described to get the points you're looking for. – Prismatic Dec 02 '12 at 03:42
  • Things just got really complicated.. Ok to start, what is with the vectors? Not to sound ignorant or anything, but I really just started 3D anything so most of this is new terminology. – CoderTheTyler Dec 02 '12 at 03:49

2 Answers2

8

Compute the center points of the near and far planes:

    vec3 nearCenter = camPos - camForward * nearDistance;
    vec3 farCenter = camPos - camForward * farDistance;

Compute the widths and heights of the near and far planes:

    real nearHeight = 2 * tan(fovRadians/ 2) * nearDistance;
    real farHeight = 2 * tan(fovRadians / 2) * farDistance;
    real nearWidth = nearHeight * viewRatio;
    real farWidth = farHeight * viewRatio;

Compute the corner points from the near and far planes:

    vec3 farTopLeft = farCenter + camUp * (farHeight*0.5) - camRight * (farWidth*0.5);
    vec3 farTopRight = farCenter + camUp * (farHeight*0.5) + camRight * (farWidth*0.5);
    vec3 farBottomLeft = farCenter - camUp * (farHeight*0.5) - camRight * (farWidth*0.5);
    vec3 farBottomRight = farCenter - camUp * (farHeight*0.5) + camRight * (farWidth*0.5);

    vec3 nearTopLeft = nearCenter + camY * (nearHeight*0.5) - camX * (nearWidth*0.5);
    vec3 nearTopRight = nearCenter + camY * (nearHeight*0.5) + camX * (nearWidth*0.5);
    vec3 nearBottomLeft = nearCenter - camY * (nearHeight*0.5) - camX * (nearWidth*0.5);
    vec3 nearBottomRight = nearCenter - camY * (nearHeight*0.5) + camX * (nearWidth*0.5);

Compute each plane from any three corners of the plane, wound CW or CCW to point inward (depending on coordinate system).

    vec3 p0, p1, p2;

    p0 = nearBottomLeft; p1 = farBottomLeft; p2 = farTopLeft;
    vec3 leftPlaneNormal = Normalize(Cross(p1-p0, p2-p1));
    vec3 leftPlaneOffset = Dot(leftPlaneNormal, p0);

    p0 = nearTopLeft; p1 = farTopLeft; p2 = farTopRight;
    vec3 topPlaneNormal = Normalize(Cross(p1-p0, p2-p1));
    vec3 topPlaneNormal = Dot(topPlaneNormal , p0);

    p0 = nearTopRight; p1 = farTopRight; p2 = farBottomRight;
    vec3 rightPlaneNormal = Normalize(Cross(p1-p0, p2-p1));
    vec3 rightPlaneNormal = Dot(rightPlaneNormal , p0);

    p0 = nearBottomRight; p1 = farBottomRight; p2 = farBottomLeft;
    vec3 bottomPlaneNormal = Normalize(Cross(p1-p0, p2-p1));
    vec3 bottomPlaneNormal = Dot(bottomPlaneNormal , p0);
orfdorf
  • 980
  • 9
  • 13
  • What does camForward represent? I'm sure camPos is the position in the coordinate system, but camForward is unclear to me. – Andreas Willadsen Jul 11 '17 at 10:59
  • I think it means the direction the camera is looking in. (http://tanrobby.github.io/note/opengl/gluLookAt.jpg) – shmoo6000 Dec 08 '17 at 19:11
  • a bit late, but shouldn't the first two formulas for the centers be with a + sign instead of a - ? If we subtract the cam forward from the cam position, wouldn't that send the near plane behind the camera opposite to the viewing direction ? – Martin Spasov Aug 04 '19 at 14:12
  • @Martin Spasov I believe that is because the answer assumes -z axis goes into the screen. – jackw11111 Jan 24 '20 at 02:27
  • vec3 leftPlaneOffset = Dot(leftPlaneNormal, p0); what does this dot product means? vec3 leftPlaneOffset = Dot(leftPlaneNormal, p0); vec3 leftPlaneOffset = Dot(leftPlaneNormal, p0); – Icaro Amorim Mar 07 '22 at 23:10
  • Why does this answer have both `camUp` and `camY`? – Stefnotch Jun 09 '23 at 16:59
5

I think the general problem you're looking to solve is how to rotate an object in 3d. From what I understand, you know how to get the magnitude of your camera's vectors, but not their orientation. You have angular rotations defined about the x,y and z axes that you want to apply to your camera's [up],[side] and [view/lookAt] vectors.

enter image description here

The above picture illustrates what I mean by up, side and lookAt vectors. They're relevant to your frustum as shown in the below pic.

enter image description here

Here is some rough code in C++ that'll rotate a point given an axis and an angle:

    Vec3 RotatedBy(Vec3 const &axisVec, double angleDegCCW)
    {
        if(!angleDegCCW)
        {   return Vec3(this->x,this->y,this->z);   }

        Vec3 rotatedVec;
        double angleRad = angleDegCCW*3.141592653589/180.0;
        rotatedVec = this->ScaledBy(cos(angleRad)) +
                     (axisVec.Cross(*this)).ScaledBy(sin(angleRad)) +
                     axisVec.ScaledBy(axisVec.Dot(*this)).ScaledBy(1-cos(angleRad));

        return rotatedVec;
    }

Once you have the rotated up, view and side vectors you can find your far plane's corners.

Prismatic
  • 3,338
  • 5
  • 36
  • 59
  • Ok so in the code, const is the original vector containing the coordinates of the point. What would axisVec be? I assume it is another vector. Very good explanation though. I must say that it did clear quite a bit up :D. So thanks! – CoderTheTyler Dec 02 '12 at 04:17
  • 'const' is a qualifier in c++, its just a way of saying that "axisVec" won't be modified in the function. The two inputs are (axisVec and angleDegCCW). I'm not really familiar with lwjgl but if you're using Java, there are probably libraries out there that have robust functions for 3d game programming. – Prismatic Dec 02 '12 at 04:27
  • LWJGL of course! I like trying to figure these things out without the help of other libraries (hence the asking). Although I can't say I understand C++ very much at all. I get the general jist of it. I guess I just have to figure out this vector math, and I'll be good. Thanks for your time and answer! – CoderTheTyler Dec 02 '12 at 04:34