Transformation from unit cube to view frustum
I'm working on a problem where I want to manage particle systems inside the view frustum, only.
Now, to do this, I've decided the most efficient way would be to represent my particles inside of a unit cube, and to use a transformation to move that cube from untransformed eyespace to the world space. My thought was that I could use a concatenation of the projection and modelview matrices to do this, but that doesn't seem to work, though it's more than possible that I screwed it up.
Here's an illustration of what I'm looking to do:
I *know* I've seen OpenGL demos which showed a frustum box for different cameras, so it seems possible to me. I guess, then, my question is what matrices do I need to use, and in what manner? I'm a little flummuxed, to be honest.
Now, to do this, I've decided the most efficient way would be to represent my particles inside of a unit cube, and to use a transformation to move that cube from untransformed eyespace to the world space. My thought was that I could use a concatenation of the projection and modelview matrices to do this, but that doesn't seem to work, though it's more than possible that I screwed it up.
Here's an illustration of what I'm looking to do:
I *know* I've seen OpenGL demos which showed a frustum box for different cameras, so it seems possible to me. I guess, then, my question is what matrices do I need to use, and in what manner? I'm a little flummuxed, to be honest.
You need to look into the projection and modelview transformation matrices. OpenGL does pretty much what you are trying to do with all the matrix stuff, except it does not transform to the unit cube, but a cube where x and y range from 1 to +1 and z ranges from 0 to 1.
But, what I am more interested in, is what did you use to make that drawing?
But, what I am more interested in, is what did you use to make that drawing?
Heh. I sketched it in flash with my wacom ( I *am* a graphic designer after all )
So, anyway, I'm using the modelview and projection matrices, but so far I'm not having much luck. I'll post code when I have a chance.
So, anyway, I'm using the modelview and projection matrices, but so far I'm not having much luck. I'll post code when I have a chance.
Some details, but first a couple screenshots:
What I'm getting:
What I'm aiming for:
So, here's some code:
So, as you can see from the first screenshot, I *do* get a box which follows the "rainview" camera's position and orientation. But it's a box, not a perspective representing frustum.
Any ideas?
EDIT: Here's how my camera's projection matrix is set up:
What I'm getting:
What I'm aiming for:
So, here's some code:
Code:
void displayRainViewCamera( void )
{
/*
Get the "rainview" camera
*/
if ( CameraCollection::currentCamera()>name() != CAMERA_RAINVIEW )
{
Camera *rv = CameraCollection::cameraNamed( CAMERA_RAINVIEW );
vec3 pos( rv>position() ), look( rv>looking());
/*
Draw a sphere at its position, and a line going
in the direction the camera's facing
*/
glPushMatrix();
glTranslatef( pos.x, pos.y, pos.z );
glColor3f( 0.75, 0.75, 1 );
glutSolidSphere( 2, 10, 10 );
glBegin( GL_LINES );
glVertex3f( 0,0,0 );
glVertex3fv( (look * 10 ).v );
glEnd();
glPopMatrix();
/*
Make a large cube. I will use a unit cube, but for
now I'm making a cube of size 40, with z=0 at the near plane,
like DoG suggests.
*/
float size = 20;
vec3 cube[8] =
{
vec3( size, size, 0 ),
vec3( size, size, 0 ),
vec3( size, size, 0 ),
vec3( size, size, 0 ),
vec3( size, size, size ),
vec3( size, size, size ),
vec3( size, size, size ),
vec3( size, size, size )
};
/*
Get the modelview and projection matrices from
the "rainview" camera ( this is *not* what's being used by the
current camera ). Get their inverses, and concatenate.
NOTE: My cameras use an infinite projection matrix for
stencil shadows. The method projectionWithFarPlane() gives
you a projection matrix with a "fake" far plane, which
I've set to 500 elsewhere.
*/
mat4 projection( rv>projectionWithFarPlane() ),
modelview( rv>modelview() ),
projectionInverse( projection.inverse() ),
modelviewInverse( modelview.inverse() );
mat4 m = modelviewInverse * projectionInverse;
/*
Transform the cube's points
*/
for ( int i = 0; i < 8; i++ )
{
cube[i] = m * cube[i];
}
/*
Draw it as lines
*/
glColor3f( 1, 0.75, 0.75 );
glBegin( GL_LINE_LOOP );
glVertex3fv( cube[0] );
glVertex3fv( cube[1] );
glVertex3fv( cube[2] );
glVertex3fv( cube[3] );
glEnd();
glBegin( GL_LINE_LOOP );
glVertex3fv( cube[4] );
glVertex3fv( cube[5] );
glVertex3fv( cube[6] );
glVertex3fv( cube[7] );
glEnd();
glBegin( GL_LINES );
glVertex3fv( cube[0] );
glVertex3fv( cube[4] );
glVertex3fv( cube[1] );
glVertex3fv( cube[5] );
glVertex3fv( cube[2] );
glVertex3fv( cube[6] );
glVertex3fv( cube[3] );
glVertex3fv( cube[7] );
glEnd();
}
}
So, as you can see from the first screenshot, I *do* get a box which follows the "rainview" camera's position and orientation. But it's a box, not a perspective representing frustum.
Any ideas?
EDIT: Here's how my camera's projection matrix is set up:
Code:
<snip>
mat4 Camera::projectionWithFarPlane( void )
{
mat4 proj( _projection );
proj.mat[10] = (( _farPlane + _nearPlane ) / ( _farPlane  _nearPlane ));
proj.mat[14] = (( 2.0f * _farPlane * _nearPlane ) / ( _farPlane  _nearPlane ));
return proj;
}
<snip>
void Camera::updateFOV( void )
{
const float aspect = (float) _width / (float) _height;
const float cotan = 1.0f / tanf( _fov * DEG2RAD );
/*
Create an infinite projection matrix, with far = infinity
See NVIDIA's "Practical and Robust Stencil Shadow Volumes for
HardwareAccelerated Rendering"
*/
_projection.zero();
_projection.mat[0] = cotan / aspect;
_projection.mat[5] = cotan;
_projection.mat[14] = 2.0f * _nearPlane;
_projection.mat[10] = 1;
_projection.mat[11] = 1;
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( _projection.mat );
_fovChanged = false;
}
There was a long silence...
'I claim them all,' said the Savage at last.
hangt5 Wrote:Is this what your looking for?
Plane Extraction
Note:Scroll down for openGL info
Actually, I've read that paper and it's the basis of my frustum culled quadtree scenegraph.
The thing is, it's not specifically what I need. I need a matrix transform which will transform a unit cube at the origin to the view frustum ( translated to wherever the camera is, and along the camera's direction ).
So, I decided that I'm probably not going to be able to make a magic matrix to perform the perspective division in eyespace, so transforming a unitcube to correlate to the world space frustum is likely not to be possible.
That said, I figured I could create a nontransformed frustum in eyespace, using info from the camera such as fov, width/height aspect, etc. I managed to create a frustum which works for all valid FOV, but fails when the aspect diverges from 1.0.
Can anybody look over it and tell me why it fails for non1.0 aspect ratios?
And to draw the frustum:
This code works, so long as the aspect ratio is 1. I thought I got my trig right creating the box, but it's just slightly off. Anybody have any ideas?
That said, I figured I could create a nontransformed frustum in eyespace, using info from the camera such as fov, width/height aspect, etc. I managed to create a frustum which works for all valid FOV, but fails when the aspect diverges from 1.0.
Can anybody look over it and tell me why it fails for non1.0 aspect ratios?
Code:
/*
Get camera params
*/
EyeSpaceFrustum::EyeSpaceFrustum( Camera *camera )
:_near( camera>nearPlane() ), _far( camera>farPlane() ),
_fov( camera>FOV() ), _nearWidth( camera>screenWidth() ),
_nearHeight( camera>screenHeight() )
{}
<snip>
void EyeSpaceFrustum::createBox( vec4 points[8] )
{
float aspect = _nearWidth / _nearHeight;
float hNearWidth = 1.0 / 2.0,
hNearHeight = ( 1.0 / aspect ) / 2.0;
printf( "aspect: %f\nhNearWidth: %f\nhNearHeight: %f\n", aspect, hNearWidth, hNearHeight );
/*
Basic trig, tan(theta) = opposite / adjacent
tan( theta ) = width / focalLength
focalLength = width / tan( theta )
*/
float tanHFov = tanf( _fov * 0.5f * DEG2RAD );
float focalLength = hNearWidth / tanHFov;
float nearDistance = focalLength,
farDistance = focalLength + (_far  _near),
farOverNear = farDistance / nearDistance;
printf( "fov: %f\n", _fov );
printf( "\n\n\n" );
points[0] = vec4( hNearWidth, hNearHeight / aspect, _near, 1 );
points[1] = vec4( hNearWidth, hNearHeight / aspect, _near, 1 );
points[2] = vec4( hNearWidth, hNearHeight / aspect, _near, 1 );
points[3] = vec4( hNearWidth, hNearHeight / aspect, _near, 1 );
points[4] = vec4( hNearWidth * farOverNear, hNearHeight * farOverNear / aspect, _far, 1 );
points[5] = vec4( hNearWidth * farOverNear, hNearHeight * farOverNear / aspect, _far, 1 );
points[6] = vec4( hNearWidth * farOverNear, hNearHeight * farOverNear / aspect, _far, 1 );
points[7] = vec4( hNearWidth * farOverNear, hNearHeight * farOverNear / aspect, _far, 1 );
}
And to draw the frustum:
Code:
/*
Draw the camera's frustum. Furst, create an EyeSpaceFrustum
and hand it the rainview camera, so it can get the camera's
properties.
*/
EyeSpaceFrustum esf( camera );
vec4 frustum[8];
esf.createBox( frustum );
/*
Multiply the box's points by the inverse modelview,
to project it into world space
*/
mat4 modelviewInverse( camera>modelview().inverse() );
for ( int i = 0; i < 8; i++ )
{
frustum[i] = modelviewInverse * frustum[i];
}
/*
Draw the frustum, with the near color as pink
and the far color ay cyan, smooth blended.
*/
vec3 nearColor( 1, 0.5, 0.5 ),
farColor( 1, 0, 1 );
glShadeModel( GL_SMOOTH );
// near
glColor3fv( nearColor );
glBegin( GL_LINE_LOOP );
glVertex3fv( frustum[0] );
glVertex3fv( frustum[1] );
glVertex3fv( frustum[2] );
glVertex3fv( frustum[3] );
glEnd();
// far
glColor3fv( farColor );
glBegin( GL_LINE_LOOP );
glVertex3fv( frustum[4] );
glVertex3fv( frustum[5] );
glVertex3fv( frustum[6] );
glVertex3fv( frustum[7] );
glEnd();
// connectors
glBegin( GL_LINES );
glColor3fv( nearColor );
glVertex3fv( frustum[0] );
glColor3fv( farColor );
glVertex3fv( frustum[4] );
glColor3fv( nearColor );
glVertex3fv( frustum[1] );
glColor3fv( farColor );
glVertex3fv( frustum[5] );
glColor3fv( nearColor );
glVertex3fv( frustum[2] );
glColor3fv( farColor );
glVertex3fv( frustum[6] );
glColor3fv( nearColor );
glVertex3fv( frustum[3] );
glColor3fv( farColor );
glVertex3fv( frustum[7] );
glEnd();
glShadeModel( GL_FLAT );
This code works, so long as the aspect ratio is 1. I thought I got my trig right creating the box, but it's just slightly off. Anybody have any ideas?
I'm not sure what the original question is really. If you want a matrix that'll take world space to a cube, that's MVP, as DoG said in post #2. The cube in question is the radius1 cube, so if you want a unit cube you'd have to do an extra scale operation, but it's quite straightforward.
If that's not what you're after, what is it that you want?
If that's not what you're after, what is it that you want?
It was a hard question to formulate. I wanted to project a unit cube from a cube, in eyespace, to the frustum in world space. I got it positioned and oriented easily enough using the modelview, but I couldn't get it to form a trapezoidal/pyramidical shape like the frustum.
Anyway, I solved it. It was a matter of generating a nontransformed frustum in eye space and positioning it with the inverse modelview of the camera.
I got it working about 5 minutes ago:
The key was my misunderstanding of the focal point of the projection. For various reasons I thought the focal point was behind the camera. I had made it too complicated. This works, and is easily represented in such a manner that I can do fast inside/outside checks, which is what I need it for, to create infrustum particle effects.
See, I'd just do it all in worldspace, but keeping it in eyespace makes the math simpler for particle clipping and the respawning hoo hah I intend to do.
Anyway, I solved it. It was a matter of generating a nontransformed frustum in eye space and positioning it with the inverse modelview of the camera.
I got it working about 5 minutes ago:
Code:
void EyeSpaceFrustum::createBox( vec4 points[8] )
{
/*
Basic trig, tan(theta) = opposite / adjacent
theta = 1/2 fov
adjacent = near or far distance
opposite = plane width, or height
*/
float tanHFov = tanf( _fov * 0.5f * DEG2RAD );
float aspect = _nearWidth / _nearHeight;
float hNearWidth = tanHFov * _near * aspect,
hNearHeight = tanHFov * _near,
hFarWidth = tanHFov * _far * aspect,
hFarHeight = tanHFov * _far;
points[0] = vec4( hNearWidth, hNearHeight, _near, 1 );
points[1] = vec4( hNearWidth, hNearHeight, _near, 1 );
points[2] = vec4( hNearWidth, hNearHeight, _near, 1 );
points[3] = vec4( hNearWidth, hNearHeight, _near, 1 );
points[4] = vec4( hFarWidth, hFarHeight, _far, 1 );
points[5] = vec4( hFarWidth, hFarHeight, _far, 1 );
points[6] = vec4( hFarWidth, hFarHeight, _far, 1 );
points[7] = vec4( hFarWidth, hFarHeight, _far, 1 );
}
The key was my misunderstanding of the focal point of the projection. For various reasons I thought the focal point was behind the camera. I had made it too complicated. This works, and is easily represented in such a manner that I can do fast inside/outside checks, which is what I need it for, to create infrustum particle effects.
See, I'd just do it all in worldspace, but keeping it in eyespace makes the math simpler for particle clipping and the respawning hoo hah I intend to do.
I still think you just wanted MVP, or maybe the inverse. Maybe I'm just being dense, though.
Well, I actually had tried MVP, but it gave me trouble when I tried to take into account screen aspect and other factors. Given your cred here, however, I'm more apt to assume *I* was being dense than you.
That said, I got it working and now have a surprisingly good volumetric rain demo:
Looking from the rain camera
Looking from a different camera, at the rain centered on the rain camera
I ended up going with a box, rather than a frustum, since it made for much faster boundary checking as well as an easier density calculation. What's cool is you can specify desired particle density and it'll increase or decrease the number of particles to maintain that target density. So you can muss with your FOV at runtime and it'll still look right.
Plus, what's most AWESOME is that as you swing the camera around, or move about, it looks completely right. You look up and the rain falls on you, you look down and you see the rain fall and converge. It looks so much better than the crappy planarrain I've seen in old games. And what I'm totally stoked about is that I can adapt this super easily to do volumetric patchy fog, like in Silent Hill 2, for PS2, but hopefully less oppressive.
Now, I'm working on integrating it gracefully with my game.
EDIT:
W00t!
Looks good, runs fast.
That said, I got it working and now have a surprisingly good volumetric rain demo:
Looking from the rain camera
Looking from a different camera, at the rain centered on the rain camera
I ended up going with a box, rather than a frustum, since it made for much faster boundary checking as well as an easier density calculation. What's cool is you can specify desired particle density and it'll increase or decrease the number of particles to maintain that target density. So you can muss with your FOV at runtime and it'll still look right.
Plus, what's most AWESOME is that as you swing the camera around, or move about, it looks completely right. You look up and the rain falls on you, you look down and you see the rain fall and converge. It looks so much better than the crappy planarrain I've seen in old games. And what I'm totally stoked about is that I can adapt this super easily to do volumetric patchy fog, like in Silent Hill 2, for PS2, but hopefully less oppressive.
Now, I'm working on integrating it gracefully with my game.
EDIT:
W00t!
Looks good, runs fast.
Possibly Related Threads...
Thread:  Author  Replies:  Views:  Last Post  
When to create custom OpenGL view instead of subclass NSOpenGL view  Coyote  37  38,440 
Oct 20, 2009 08:16 PM Last Post: Coyote 

Cube autorotate  sakrist  2  4,299 
May 5, 2009 03:31 AM Last Post: Ingemar 

OpenGL ES  Drawing a simple cube help.  MattCairns  7  15,681 
Oct 10, 2008 05:26 PM Last Post: Frogblast 

New to OpenGL  Transformation Help  Dash Rantic  4  4,319 
Mar 9, 2008 06:08 PM Last Post: Dash Rantic 

Pregenerated cube map  Fenris  2  4,578 
Apr 5, 2007 07:01 PM Last Post: nich 