# oblique frustum

I’ve been trying to implement nVidia’s “oblique frustum culling” demo’s technique in my engine for clipping at waterplane, but without luck. Mostly becouse I don’t understand the math bedind it.
I want to create a function that takes old projection matrix, old modelview matrix (from demo looks like I need it) and some clip plane and outputs new projection matrix.

Demo looks like it’s building a modification(shear?) matrix using old inverted&trnsponded MVP. I have no idea why/how this works. Can someone give me some info about it. Or some links that explain this.

Can anyone give me a little help with this. Like someone who wrote the demo or implemented it?

I also would like to know something about it. I hear d some rumors about this kind of clipping for months and I’m courious.

``````Let P = (Px, Py, Pz, Pw) be the eye-space plane to which you would like to clip.
This will replace your ordinary near plane and should be facing away from the
camera, so Pz < 0.

Let Q be an eye-space point.  If P dot Q = 0, then Q lies on the plane P.

What we want to do is modify the projection matrix M so that points Q for
which P dot Q = 0 get transformed in such a way that the transformed z-coordinate
is the negation of the transformed w-coordinate.  This corresponds to
the projected z-coordinate that you would ordinarily get for a point lying
on the near plane.  After division by the w-coordinate, you get -1.

The projection matrix M transforms points from eye space into homogeneous clip
space.  To obtain the clip-space plane P', we need to multiply P by the inverse
transpose of M.  (This is because planes are covariant vectors.)  The clip-space
plane P' is given by

-1 T
P' = (M  )  P

Let Q' be the projection of the point Q, given by Q' = MQ.  If Q lies on the plane P,
then P' dot Q' = 0, but we want it to be -Q'w so that it corresponds to the near plane.
So we force it by subtracting 1 from the w-coordinate of P'.  Before doing this,
however, we want to divide P' by its z-coordinate as to avoid scaling the z
direction in clip space.  Doing all of this gives us the following plane P":

P'x    P'y       P'w
P" = (-----, -----, 1, ----- - 1)
P'z    P'z       P'z

We integrate this into the projection matrix M by constructing a new projection
matrix M' as follows.

[  1    0    0    0  ]
[                    ]
[  0    1    0    0  ]
M' = [                    ] M
[ P"x  P"y   1   P"w ]
[                    ]
[  0    0    0    1  ]

The matrices M and M' only differ in the entries of the 3rd row, so we could just
calculate replacements for 3rd-row entries of M as follows:

M'(3,1) = P" dot M_1
M'(3,2) = P" dot M_2
M'(3,3) = P" dot M_3
M'(3,4) = P" dot M_4

where M_i means the i-th column of M and M'(i,j) means the (i,j)-th entry of M'.
``````

I just now noticed reply. Thank you Eric, that was most helpfull.

I’m actually doing some more research on this technique at the moment. It turns out that the far plane is completely hosed, and your z-buffer precision is cut roughly in half. These effects can be minimized with a little more work – I’ll make another post once I’ve nailed everything down myself. The above technique should work pretty well in most real-world cases.

``````Okay -- here's the short, short version of the optimal implementation of the oblique frustum.

Let C = camera-space clipping plane. Assume the camera is on the negative side of the plane: C_w < 0.

Let M be the original projection matrix; M must be invertible, but otherwise we don't care what
it is. We'll modify the third row of M so that the near plane coincides with the arbitrary
clipping plane C.  We aren't allowed to modify the fourth row of M because doing so would screw
up the perspective-correct vertex attribute interpolation. (The fourth row usually just
moves the z-coordinate of a camera-space point into the w-coordinate of a clip-space point.)

Given a projection matrix M, the near plane is always M_4 + M_3, and the far plane is always
M_4 - M_3. (M_i means the i-th row of M.)  To force the near plane to coincide with the
clipping plane C, we must have M_3 = aC - M_4, where a is some positive scale factor that we

Why adjust a?  Because now our far plane F has been moved to F = 2*M_4 - aC, which is not
generally parallel to C.  F intersects C on the x-y plane, so it's really not in a good
position.  The best we can do is minimize the size of the view frustum by choosing the
constant a so that F contains the corner Q of the view frustum opposite the near plane C.
This point Q is given by

Q = M^-1 * (sgn(C_x), sgn(C_y), 1, 1),

where M^-1 is the inverse of the projection matrix. (The points (+/-1, +/-1, 1, 1) are the
four corners of the view frustum on the far plane in clip space.)  The scale factor a is now
given by

a = (2*M_4 dot Q) / (C dot Q).

Since M_4 is usually (0, 0, -1, 0), this can be simplified a little.

All we have to do to the original projection matrix M is replace the third row with aC - M_4.

-- Eric Lengyel
``````

Thank you very much for this. I still have to take a in-depth look at it since I’m pretty full of stuff to do but I appreciated your contribution a lot.

Thank you Eric!

Sample code for this technique is now posted on terathon.com .

Hi Eric, i’ve noticed the code you published in gpg5 and the code in theraton.com are different. In particular the line (in theraton.com)

``````
Vector4D c = clipPlane * (2.0F / Dot(clipPlane, q));

``````

is

``````
Vector4D c = clipPlane * (-2.0F / Dot(clipPlane, q));

``````

in GPG5, anyway none of them work for me, i don’t know exactly why yet, but keep on looking for the error

Ok, i am particulary idiot, 2 hours after posting that i discovered the error, now seems oblique frustum works At least it helped me to detect several caveats in my algorithsm and my way of thinking Toni

P.S I still don’t know why that -2 is different in GPG tho