Mac, iPhone, iPad & iPod Game Developers Forum - iDevGames  


Graphics & Audio Programming All aspects of coding 2D/3D graphics.

Reply
 
Thread Tools Search this Thread Display Modes
Old
  (#1)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
Transformation from unit cube to view frustum - 2005.07.07, 10:50 AM

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 eye-space 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.
   
Reply With Quote
Old
  (#2)
DoG
Moderator
DoG is on a distinguished road
 
DoG's Avatar
 
Posts: 942
Join Date: 2003.01
Location: The Old World
2005.07.07, 03:48 PM

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?
   
Reply With Quote
Old
  (#3)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.07, 04:19 PM

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.
   
Reply With Quote
Old
  (#4)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.07, 06:15 PM

Some details, but first a couple screenshots:

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 
                Hardware-Accelerated 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;
}

Last edited by TomorrowPlusX : 2005.07.07 at 07:03 PM.
   
Reply With Quote
Old
  (#5)
hangt5
Member
hangt5 is on a distinguished road
 
hangt5's Avatar
 
Posts: 195
Join Date: 2004.12
Location: San Francisco
2005.07.07, 08:56 PM

Is this what your looking for?

Plane Extraction

Note:Scroll down for openGL info


There was a long silence...
'I claim them all,' said the Savage at last.
   
Reply With Quote
Old
  (#6)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.08, 07:55 AM

Quote:
Originally Posted by hangt5
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 ).
   
Reply With Quote
Old
  (#7)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.11, 07:58 AM

So, I decided that I'm probably not going to be able to make a magic matrix to perform the perspective division in eye-space, so transforming a unit-cube to correlate to the world space frustum is likely not to be possible.

That said, I figured I could create a non-transformed frustum in eye-space, 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 non-1.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?
   
Reply With Quote
Old
  (#8)
OneSadCookie
Member
OneSadCookie is on a distinguished road
 
OneSadCookie's Avatar
 
Posts: 4,833
Join Date: 2002.04
Location: Darkest California
2005.07.11, 08:37 AM

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 radius-1 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?
   
Reply With Quote
Old
  (#9)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.11, 10:50 AM

It was a hard question to formulate. I wanted to project a unit cube from a cube, in eye-space, 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 non-transformed 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 in-frustum 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.
   
Reply With Quote
Old
  (#10)
OneSadCookie
Member
OneSadCookie is on a distinguished road
 
OneSadCookie's Avatar
 
Posts: 4,833
Join Date: 2002.04
Location: Darkest California
2005.07.11, 06:48 PM

I still think you just wanted MVP, or maybe the inverse. Maybe I'm just being dense, though.
   
Reply With Quote
Old
  (#11)
TomorrowPlusX
Member
TomorrowPlusX is on a distinguished road
 
TomorrowPlusX's Avatar
 
Posts: 1,392
Join Date: 2004.10
Location: Washington DC
2005.07.12, 03:32 PM

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 planar-rain 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.

Last edited by TomorrowPlusX : 2005.07.12 at 06:06 PM.
   
Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump



Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
vBulletin Skin developed by: vBStyles.com
DevServe Network: iDevApps | uDevGames | iDevGames