Projection - project out instead of in, how?

I’ve been researching this for a while now, and I’m not really getting anywhere.

Remember the old formula for perspective?

x=x*(1000/z);
y=y*(1000/z);

I want the reverse effect like this formula:

x=x*(z/1000);
y=y*(z/1000);

I want to do that very same thing with the projection matrix. I want it to scale away from the center of the screen intead of toward.

A simple projection matrix looks like this:

m[0]=1.0f;
m[1]=0.0f;
m[2]=0.0f;
m[3]=0.0f;
m[4]=0.0f;
m[5]=1.0f;
m[6]=0.0f;
m[7]=0.0f;
m[8]=0.0f;
m[9]=0.0f;
m[10]=1.0f;
m[11]=0.0f;
m[12]=0.0f;
m[13]=0.0f;
m[14]=1000.0f;
m[15]=1.0f;

However, the projection matrix that OpenGL creates with glFrustum looks like this:

n=near clipping plane
f=far clipping plane
l=left clipping plane
r=right clipping plane
t=top clipping plane
b=bottom clipping plane

m[0]=2n/(r-l);
m[1]=0.0f;
m[2]=0.0f;
m[3]=0.0f;
m[4]=0.0f;
m[5]=2
n/(t-b);
m[6]=0.0f;
m[7]=0.0f;
m[8]=(r+l)/(r-l);
m[9]=(t+b)/(t-b);
m[10]=-(f+n)/(f-n);
m[11]=-1.0f;
m[12]=0.0f;
m[13]=0.0f;
m[14]=-2fn/(f-n);
m[15]=0.0f;

I need to modify the above matrix so that it projects outward instead of inward…but how??? I’ve been playing with this thing for two days now. It’s a simple as swapping 1000 with z in the old formula. It should be just as simple with the matrix but I can’t seem to pull it off .

[This message has been edited by WhatEver (edited 01-24-2003).]

You can’t do this with the projection matrix. Sorry.

In homogenous coordinates, a vertex position is (x/w, y/w, z/w). The projection matrix sets it so that this calculates (x/z, y/z, depth). You can divide over a linear combination of your vertex x/y/z/w, but you cannot multiply.

You could probably do it with a vertex program or by using software to preprocess your vertices.

Ok, that’s alright. Is there any way to take the near, far and fov parameters and create a deviser for the old formula? Right now this formula works for only 800x600 with a 60 degree fovy:

x=-x*(z/518.4810127f);
y=-y*(z/518.4810127f);

I need to derive 518.4810127 mathamaticly from the same info you give glFrustum. I got that number from deviding my far plane 2048 by 3.95. I found 3.95 by deviding 2048 by an approximation I found by trial and error.

So the effect would be something like the inverse perspective in some of David Hockney’s paintings?

This is a total guess, but maybe you could achieve a similar effect by inverting depth test (using GL_GREATER) and mirroring your geometry. Maybe.

-Ilkka

I think the only way I’m going to pull this off is by raycasting. I wanted a faster way, but I think that I’ll just rotate the point instead of scale it. That should work. I’ll try it later.

Thanks for your help guys.

I believe you can do this with the depth test set to greater. You just need to add an extra transform onto the projection matrix.

Basically you push the regular projection on, then you push on a matrix that mirrors eye space such that the near and far clip planes flip. It would work like this:

glLoadIdentity();
gluProject( …);
glTranslatef( 0.0f, 0.0f, -(near+far)/2.0f);
glScalef( 1.0f, 1.0f, -1.0f);
glTranslatef( 0.0f, 0.0f, (near+far)/2.0f);

-Evan

Ok, I’m officially going crazy. Every theory I have, and every sample source I use, I can’t get to work.

I MUST figure this out for my terrain editor. I don’t want to restrict picing in ortho mode. Every 3D editor I know of has perspective picking, but I can’t find any resources that explain it well. I’ve even looked through my old game programming books yet I can still not figure it out because OpenGL uses a Projection matrix not that old formula like in my first post.

Here’s was a briliant theory that didn’t work. The idea was to multiply a -zdepth of the ray with the projection matrix. This would create a point behind the view point which would be scaled out instead of in…BUT, it didn’t work! Bah!

Here’s my code:

S3Dvoid s3d_camera::ScreenToWorld(S3Dfloat xin, S3Dfloat yin, S3Dfloat zin, S3Dfloat* xout, S3Dfloat* yout, S3Dfloat* zout)
{
	S3Dmat16f	Projection;
	S3Dint		ViewPort[4];
	S3Dvec3f		Pos;
	S3Dfloat		Temp;

	//get the viewport dimentions
	glGetIntegerv(GL_VIEWPORT, ViewPort);

	//get the projection matrix
	glGetFloatv(GL_PROJECTION_MATRIX, Projection);

	//4rth emement must be 1.0f for correct matrix multiplication
	Pos[3]=1.0f;

	//flip y
	yin=ViewPort[3]-yin;

	//orientate point relative to the center of the viewport
	xin=xin-(ViewPort[2]*0.5f);
	yin=yin-(ViewPort[3]*0.5f);

	//assign x, y and z to vector
	s3dVecSet3f(Pos, xin, yin, zin);

	//scale x and y position out the further away the vertex is from the screen
	//this method only works at 800x600 and a 60 degree fov
	//Pos[0]=-Pos[0]*Pos[2]/518.4810127f;//60 degrees
	//Pos[1]=-Pos[1]*Pos[2]/518.4810127f;

	//we don't want to change the z value, just the x and y value so store the
	//original z value in a temp float
	Temp=Pos[2];

	//place point behind viewer so it scale outward instead of inward
	Pos[2]=-Pos[2];

	//project, aka scale x and y.
	s3dMatMultiply4x16f(Pos, Projection);

	//restore z value
	Pos[2]=Temp;

	//move the Pos into world space
	s3dMatMultiply4x16f(Pos, CameraMatrix);
	
	*xout=Pos[0];
	*yout=Pos[1];
	*zout=Pos[2];
}

Why not simply use gluUnProject for picking?

[This message has been edited by DFrey (edited 01-25-2003).]

I can’t get it to work when the z doesn’t eqaul 0. It seems like it’s only good for projecting a point on the viewing plane and nowhere else. Am I wrong?

It can work with other z’s. The tricky part is figuring out the window z coordinates to use. See this old thread for some hints: http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/000613.html

Hmmmm, I understand gluUnproject very clearly now. Let’s see if I can get it to work.

It worked. Now I just have to get it to produce a ray and I’m all set.

Thanks DFrey!

I made a demo that uses gluUnProject for casting rays. I call gluUnproject twice; once with the z depth returned by glReadPixels and again with a z depth of zero. This creates the two points in space that I need to make a ray for ray collision detection and point selection.

The demo(runs in 1024x768): http://www.spider3d.com/dl/s3d_terrain.zip

I haven’t started on the terrain part yet, so for now I used the spitfire to test the ray casting. All you have to do is point the mouse anywhere on the spitfire and it will draw the normal of the bounding collision areas.

Have fun .