draw frustum outlines

Hi,

I have a world in which I have a bunch of cameras. I want to draw each camera view frustum as lines. In order to do that I need to know the (world-space) coordinates of the 8 vertices that make up the near- and far-plane polygons. How can I calculate these? I have the position, rotation/viewing direction vector/up vector, aspect ratio, farplane/nearplane distance etc.

Thanks in advance. :slight_smile:

As you, know the viewing frustum projects the viewvolume to the range [-1,1]x[-1,1]

Just try defining 4 vertices:

(-1,-1,scale,1.0);
(-1,1,scale,1.0);
(1,1,scale,1.0);
(1,-1,scale,1.0);

and multiply them them with the inverse of the projection matrix, that should do the trick.

N.

The transformed clip space position c of a
world space vertex v is obtained by transforming 
v with the product of the projection matrix P 
and the modelview matrix MV
  
c = P MV v
 
So, if we could solve for v, then we could 
genrerate vertex positions by plugging in clip 
space positions. For your frustum, one line 
would be between the clip space positions 
  
(-1,-1,near) and (-1,-1,far), 
 
the lower left edge of the frustum, for example.
 
NB: If you would like to mix normalized device 
coords (x,y) and eye space coords (near,far), 
you need an additional step here. Modify your 
clip position as follows
 
c' = (c.x * c.z, c.y * c.z, c.z, c.z)
 
otherwise you would need to supply both the z 
and w for c, which might be inconvenient. Simply 
use c' instead of c below.
 
 
To solve for v, multiply both sides of the equation above with 
 
      -1       
(P MV) 
 
This gives
 
      -1      
(P MV)   c = v
  
This is equivalent to
  
  -1  -1      
MV   P   c = v
  
 -1
P   is given by
  
|(r-l)/(2n)     0         0      (r+l)/(2n) |
|     0    (t-b)/(2n)     0      (t+b)/(2n) |
|     0         0         0         -1      |
|     0         0   -(f-n)/(2fn) (f+n)/(2fn)|
 
where l, r, t, b, n, and f are the parameters in the glFrustum() call.
    
If you don't want to fool with inverting the 
model matrix, the info you already have can be 
used instead: the forward, right, and up 
vectors, in addition to the eye position.
 
First, go from clip space to eye space
  
     -1   
e = P   c
 
Next go from eye space to world space
 
v = eyePos - forward*e.z + right*e.x + up*e.y
 
assuming x = right, y = up, and -z = forward.

edit:

-tried to reformat
-added a note

Try something like this:

  
Assume proj contains projection matrix and mv contains modelview matrix...
Matrix4 proj, mv, temp, inv;
temp = proj * mv;
inv = inverse(temp); // compute inverse of matrix

VECTOR4 fr[8]=
{
// near
{-1, -1, -1, 1}, { 1, -1, -1, 1}, { 1,  1, -1, 1},  {-1,  1, -1, 1},
// far
{-1, -1, 1, 1},	{ 1, -1, 1, 1},	{ 1,  1, 1, 1},  {-1,  1, 1, 1}
};
// Transform all vertices:
// multiply vertex array (fr) by matrix. result is transformed vertex array (tfr)
VECTOR4 tfr[8];
transform_points(fr, 8, inv, tfr); 

int i;
for (i=0; i<8; i++)
{
 tfr[i].x /= tfr[i].w;
 tfr[i].y /= tfr[i].w;
 tfr[i].z /= tfr[i].w;
 tfr[i].w = 1.0f;
}

glBegin(GL_LINES);
connect tfr points as follow:
0-1, 1-2, 2-3, 3-0, 
4-5, 5-6, 6-7, 7-4,
0-4, 1-5, 2-6, 3-7
glEnd();

This is a pseudo code. I suppose that you already have your math library with matrix and vector functions (inverse, transform_points and matrix_mult (*).

yooyo

Here’s a little code to demonstrate the idea
above:

Vector xyzToWorld( Vector xyz, Vector eye,  Vector forward, Vector right, Vector up ) {
 
// xyz is normalized device (x,y) and eye space z
// eye, forward, right, and up constitute the
// eye origin and basis vectors, respectively.
  
// frustum info
float nearZ   = 4;
float farZ    = 10000;
float fovX    = 90;
 
// frutum setup  
float projZ   = nearZ * tan( fovX * PI / 360 );
float aspect  = screenHeight / screenWidth;
float l       = -projZ;
float r       = +projZ;
float b       = -projZ * aspect;
float t       = +projZ * aspect;
float n       = nearZ;
float f       = farZ;
 	
// inverse of projection matrix
// based on the parameters above - the same 
// values use in the glFrustum() call.
 
float P[16] = {
(r-l)/(2*n),      0,	     0,        (r+l)/(2*n),
     0,      (t-b)/(2*n),    0,        (t+b)/(2*n),
     0,           0,         0,             -1,      
     0,           0,   -(f-n)/(2*f*n), (f+n)/(2*f*n)
};
  
// undo divide with w (eye space z)
float px = xyz.x * xyz.z;
float py = xyz.y * xyz.z;
float pw = xyz.z;
  	
// move into eye space
float ex = P[0] * px  + P[1] * py  + P[3] * pw; 
float ey = P[4] * px  + P[5] * py  + P[7] * pw;
float ez = P[8] * px  + P[9] * py  + P[11]* pw;
  
// move into world space
return eye - forward*ez + right*ex + up*ey;
}

So, for your frustum problem, you could supply these values for xyx:
(-1,-1,nearZ) (-1,-1,farZ)
(-1,+1,nearZ) (-1,+1,farZ)
(+1,+1,nearZ) (+1,+1,farZ)
(+1,-1,nearZ) (+1,-1,farZ)

with all the methods posted ill just add one more variation.

fovy, near, far are given

hheight = tan(fovy/2); //half the height of the frustum at z=1;
hwidth = hheight * aspect; //same for width

so assuming you know your cameras forward/up/right vectors (ie, using a “matrix camera” or simply invert the view matrix).

right=hwidthfarcam_right;
top=hheightfarcam_up;
center=cam_pos + far*cam_forward;

nice to have (vectors from the center of the far plane to the sides/corners)

ftl = center - right + top; //far top left
fbr = center + right - top; //far bottom right

and so on.
no need to touch any opengl matrices if you dont do anything fancy, know your fov and the cameras vectors (ok, that IS a matrix, but you usually manage that one yourself).

Now that’s exactly what I was looking for!

Thanks to you all! :slight_smile:

One thing always puzzled me - why do people use the vertical field of view to define a perspective matrix?
The more intuitive axis would be the horizontal field of view…for me at least.