PDA

View Full Version : Oblique near-plane clipping



hardtop
06-21-2006, 03:23 PM
Hi all, I've gone through Mr Eric Lengyel's tutorial on Oblique near-plane clipping, but I can't get it working. Here's what I'm doing:


void HT_Graphics::ModifyProjectionMatrix(CVector4& clipPlane)
{
float matrix[16];
CVector4 q;

float mat1[16];
float mat2[16];

glGetFloatv(GL_MODELVIEW_MATRIX, mat1);

CMatrix::mat4_inverseOrth(mat2, mat1);
CMatrix::mat4_transpose(mat1, mat2);

float xplane[4];

CMatrix::plane_transform(xplane, clipPlane.v, mat1);
CVector4 resVec(xplane[0], xplane[1], xplane[2], xplane[3]);

// Grab the current projection matrix from OpenGL
glGetFloatv(GL_PROJECTION_MATRIX, matrix);

// Calculate the clip-space corner point opposite the clipping plane
// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix

q.x = (sgn(resVec.x) + matrix[8]) / matrix[0];
q.y = (sgn(resVec.y) + matrix[9]) / matrix[5];
q.z = -1.0f;
q.w = (1.0f + matrix[10]) / matrix[14];

// Calculate the scaled plane vector
CVector4 c = resVec * (2.0f / (resVec * q));

// Replace the third row of the projection matrix
matrix[2] = c.x;
matrix[6] = c.y;
matrix[10] = c.z + 1.0f;
matrix[14] = c.w;

// Load it back into OpenGL
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix);

glMatrixMode(GL_MODELVIEW);
}
static inline void plane_transform(float* dst, float* src, float* matrix)
{
#define M(ROW,COL) matrix[((COL)*4)+(ROW)]

dst[0] = src[0] * M(0,0) + src[1] * M(1,0) + src[2] * M(2,0) + src[3] * M(3,0);
dst[1] = src[0] * M(0,1) + src[1] * M(1,1) + src[2] * M(2,1) + src[3] * M(3,1);
dst[2] = src[0] * M(0,2) + src[1] * M(1,2) + src[2] * M(2,2) + src[3] * M(3,2);
dst[3] = src[0] * M(0,3) + src[1] * M(1,3) + src[2] * M(2,3) + src[3] * M(3,3);

#undef M
}

static inline void mat4_transpose(float* dst, float* src)
{
dst[0]=src[0];
dst[1]=src[4];
dst[2]=src[8];
dst[3]=src[12];
dst[4]=src[1];
dst[5]=src[5];
dst[6]=src[9];
dst[7]=src[13];
dst[8]=src[2];
dst[9]=src[6];
dst[10]=src[10];
dst[11]=src[14];
dst[12]=src[3];
dst[13]=src[7];
dst[14]=src[11];
dst[15]=src[15];
}

static inline void mat4_inverseOrth(float* dst, float* src)
{
dst[0]=src[0];
dst[1]=src[4];
dst[2]=src[8];
dst[3]=0.0f;
dst[4]=src[1];
dst[5]=src[5];
dst[6]=src[9];
dst[7]=0.0f;
dst[8]=src[2];
dst[9]=src[6];
dst[10]=src[10];
dst[11]=0.0f;
dst[12]=-src[12]*src[0]-src[13]*src[1]-src[14]*src[2];
dst[13]=-src[12]*src[4]-src[13]*src[5]-src[14]*src[6];
dst[14]=-src[12]*src[8]-src[13]*src[9]-src[14]*src[10];
dst[15]=1.0f;
}
...
isReflection = true;
framesComputeReflection = 0;

// *** calculate reflection ************************************************** *********
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBufferReflection);

glViewport(0,0, REFLECTION_TEXTURE_SIZE, REFLECTION_TEXTURE_SIZE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

gluLookAt(pos.x, pos.y, pos.z,
view.x, view.y, view.z,
up.x, up.y, up.z);

glPushMatrix();
if(pos.y > waterHeight)
{
glTranslatef(0.0f, waterHeight * 2.0f, 0.0f);
glScalef(1.0, -1.0, 1.0);

glCullFace(GL_FRONT);

ModifyProjectionMatrix(CVector4(0.0, 1.0, 0.0, -waterHeight));

// render scene to reflection texture
HT_Graphics::SetActiveShaderProgram(HT_Constants:: SH_NONE);
m_skybox->Render(pos.x, pos.y, pos.z, engineconfig->farPlane-10.0f, engineconfig->farPlane-10.0f, engineconfig->farPlane-10.0f);

if(engineconfig->reflectionsFull)
{
m_mapI->Render();
}

ResetProjectionMatrix();

glCullFace(GL_BACK);
}
else
{
ModifyProjectionMatrix(CVector4(0.0, 1.0, 0.0, waterHeight));

if(engineconfig->reflectionsFull)
{
m_mapI->Render();
}

ResetProjectionMatrix();
}

glPopMatrix();

glBindTexture(GL_TEXTURE_2D, reflTex);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
...I don't know what's gone wrong :confused:

What I do is I pass the clipping plane equation as world coordinates (as for the glClipPlane function), and I transform the coordinates to eye coords using inverse transpose modelview matrix. Then I modify the projection matrix accordingly as described in Mr Lengyel's article.

Here's a shot of the problem
http://www.hardtopnet.net/temp/OBLIQUE.JPG

The plane still seems to "follow" the camera, and doesn't clip along the surface of the water (the brick wall is actually a "reflection" of the bottom of the pool, and the black thing is a clipped region)

Anybody got a clue?

Thanks a lot

HardTop

Java Cool Dude
06-22-2006, 12:38 AM
You need to multiply the plane's normal (in your case [0 -1 0 1]) by the inverse view matrix and then normalize it (only the xyz part leave the w out of it since you need it set to 1.0f for later).
Then you need to transform a point on the water plan again by the inverse view matrix.
Once you have those two under your belly, the new water plane will be something like (normal.xyz, -dot4(transformedPointOnAPlane, normal)).
Use that plane to modify your projection matrix =-)

CrazyButcher
06-22-2006, 02:40 AM
that didnt fully work, jcd. only mistake was that one time the transposed and one time the normal viewmatrix is used.

but I could fix my issues with your hints, and this here works

// transform clipplane to eyespace
MatrixTranspose(mviewinvT,mviewinv);
// transform normal
Vector3Transform(eyeplane,clipPlane,mviewinvT);
// transform point on normal
Vector3Scale(q,clipPlane,clipPlane[3]);
Vector3Transform1(q,mview);
eyeplane[3] = -Vector3Dot(q,eyeplane);

hardtop
06-22-2006, 05:29 AM
I'll give that a try. thanks guys. Anymore clues are welcome :)

hardtop
06-22-2006, 06:16 AM
OK but what are Vector3Transform, Vector3Scale and Vector3Transform1 supposed to do, anyway?

Jackis
06-22-2006, 10:44 AM
I'm not sure exactly, cause it's not my code )))

but it is seems to me, that

- Vector3Transform() multiplies 3-component vector by 3x3 matrix, saving the result in other vector (b = M*a)
- Vector3Scale() scales 3-component vector by constant value, saving the result in other vector (b = t*a)
- Vector3Transform1() does the same, as simple version, but the result is stored in the input vector (a = M*a)

plasmonster
06-22-2006, 10:47 AM
I think Eric's code needs the world-space clipplane transformed into eye-space (using the inverse transpose of the view matrix).

hardtop
06-22-2006, 03:32 PM
SeanG: exactly, that's what I'm trying to do, but I don't seem to get the correct coordinates computed when I multiply my world-space clipplane by the inverse transpose modelview matrix, to obtain eye-space coords

Jackis: this makes sense :)

I'm just wondering what went wrong with my calculation of the Inv-T-MVMatrix, and with my conversion from world-space to eye-space...

Thanks for your feedback! Don't hesitate to post anymore thoughts

HT

plasmonster
06-22-2006, 06:36 PM
Hmmm, this just works for me.

I think maybe he's got this thing wired for the eye on one side of the clipplane, as is the case for reflection clipping. If you want to use this as a generic clipplane (which looks to be what's going on in your scene), you may need to handle the more general case.

Try this minor modification:

// ...

Plane q;
//q.X = (Sign(clipPlane.X) + projMat[8]) / projMat[0];
//q.Y = (Sign(clipPlane.Y) + projMat[9]) / projMat[5];
//q.Z = -1;
//q.W = (1.0F + projMat[10]) / (projMat[14]);

q.X = Sign(clipPlane.X);
q.Y = Sign(clipPlane.Y);
q.Z = -Sign(clipPlane.Z);
q.W = 1;
q = q.Transform(projMat.Inverse());

// ...This is pretty much what he already has in his comments; the code is just optimized for the common case.

Please correct me if I hosed something.

Eric Lengyel
06-22-2006, 11:19 PM
Hi --

After a quick look at your code, here are the things that come to mind:

1) It looks like you're specifying a clip plane in world space and then transforming it into eye space with the inverse transpose of the model-view matrix. Does the model-view matrix actually go from world space to eye space in your case, or does it transform from some local object space?

2) The clip plane needs to face away from the camera, and I don't think it does in your code. Try negating it. In eye space, the clip plane's w-coordinate should be negative.

3) The planes that you're using for above water and below water don't match up. I think you want to use (0, -1, 0, waterHeight) above the water and (0, 1, 0, -waterHeight) below the water.

-- Eric Lengyel

hardtop
06-23-2006, 07:57 AM
SeanG: tried your solution but to no avail

Mr Lengyel:

1) I tried initializing a variable (mvm) containing the modelview matrix just after the glulookat call, but it didn't work
2) didn't work either
3) I'll try and have the "above water" case working first :)

but thanks a lot anyway. Here's another screenshot representing the issue. You can see two intersecting clipping planes in the reflection. One plane is "camera" dependent (the one producing the black background), where the other seems "fixed", but not parallel to the surface of the water

http://www.hardtopnet.net/temp/OBLIQUE2.JPG

hardtop
06-23-2006, 09:03 AM
sorry, last screenshot was not relevant.

Here's an exact version of what's happening:

http://www.hardtopnet.net/temp/OBLIQUE3.JPG

hardtop
06-23-2006, 09:39 AM
I can't get anywhere... f*cking clipping planes! I'm getting desperate :s

hardtop
06-23-2006, 11:19 AM
oooof solved at last...

It was probably an issue about that matrix column or row major order stuff... I disabled the transposition of the inverse MV matrix, and it's working at last.

Here's the code:


void HT_Graphics::ClipPlane(CVector4& clipPlane)
{
float matrix[16];
CVector4 q;

float mat1[16];
float mat2[16];
float xplane[4];

// Grab the current projection matrix from OpenGL
glGetFloatv(GL_MODELVIEW_MATRIX, mvm);
glGetFloatv(GL_PROJECTION_MATRIX, matrix);

CMatrix::mat4_inverseOrth(mat2, mvm);
//CMatrix::mat4_transpose(mat1, mat2);

CMatrix::plane_transform(xplane, clipPlane.v, mat2);
CVector4 resVec(xplane);

// Calculate the clip-space corner point opposite the clipping plane
// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
q.x = (sgn(resVec.x) + matrix[8]) / matrix[0];
q.y = (sgn(resVec.y) + matrix[9]) / matrix[5];
q.z = -1.0f;
q.w = (1.0f + matrix[10]) / matrix[14];

// Calculate the scaled plane vector
CVector4 c = resVec * (2.0f / (resVec * q));

// Replace the third row of the projection matrix
matrix[2] = c.x;
matrix[6] = c.y;
matrix[10] = c.z + 1.0f;
matrix[14] = c.w;

// Load it back into OpenGL
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix);

glMatrixMode(GL_MODELVIEW);
}Thanks to everybody for your help! I still got an issue on the nVidia board, where the fresnel term is obviously wrong, but I'll try and fix that :)


On the ATI, it's looking OK, but on the nVidia, the fresnel seems wrong http://www.hardtopnet.net/temp/FRESNEL_ATI_NVIDIA.jpg

Java Cool Dude
06-23-2006, 12:55 PM
If you're using GLSL, try using -reflect(view, normal) instead of reflect(view, normal); that should fix it on NVIDIA cards =-/

Java Cool Dude
06-23-2006, 12:59 PM
Originally posted by CrazyButcher:
that didnt fully work, jcd. only mistake was that one time the transposed and one time the normal viewmatrix is used.

but I could fix my issues with your hints, and this here works

// transform clipplane to eyespace
MatrixTranspose(mviewinvT,mviewinv);
// transform normal
Vector3Transform(eyeplane,clipPlane,mviewinvT);
// transform point on normal
Vector3Scale(q,clipPlane,clipPlane[3]);
Vector3Transform1(q,mview);
eyeplane[3] = -Vector3Dot(q,eyeplane); You need to rotate and translate your point, that would make a Vector4Transform =-/
Also -Vector4Dot instead of Vector3Dot...

plasmonster
06-23-2006, 02:54 PM
The proper transform for planes/normals is the inverse transpose, so something is amiss if an inverse is working.

Check you matrix/vector routines, carefully. If the math is done correctly, this should just work.

By the way, the general clipplane transform will work, but it'll hose your depth as you swing around the other (positive) side of the plane. Nvidia has a demo of a slightly different technique for a generalized clipplane, but it makes no attempt to maximize depth precision.

CrazyButcher
06-23-2006, 03:06 PM
thanks jcd, my vector3transform actually does translation, too. so the first eyeplane transform is actually incorrect, but the transposed matrix4x4 in my case contains 0 as translation so it works. but why would eyeplane.w need a vector4dot ?

hardtop
06-24-2006, 01:28 AM
SeanG: I think I'll perform another clipping if I get to the other side of the plane. I have an IF on the height on the plane.

HT

hardtop
06-24-2006, 03:36 AM
JCD, I tested your option to negate the reflect term, but I get a kind of "inverted" effect on the underwater textures, and a saturated effect on the reflection:


...
vec4 viewReflection = normalize( -reflect(-viewTangetSpace, normalVector) );
vec4 invertedFresnel = vec4( dot(normalVector, viewReflection ) );
vec4 fresnelTerm = 1.0 - invertedFresnel;
...And here's the shot:
http://www.hardtopnet.net/temp/REFLECT.jpg

It was tested on both ATI and nVidia

HT

hardtop
06-24-2006, 03:54 AM
Here's a shot where I only display the Fresnel term to the output color; on both GPU's. Both are clearly different. The fresnel term is computed according to the code lines in the above post. (I think the viewReflection thing is different itself)

Is there a difference in the implementation of "reflect()" on ATI and nVidia?

http://www.hardtopnet.net/temp/FRESNELTERM.jpg

hardtop
06-26-2006, 04:31 AM
up :)