Extracting View Frustum Planes (Gribb & Hartmann method)
Asked Answered
O

2

15

I have been grappling with the Gribb/Hartmann method of extracting the Frustum planes for some time now, with little success. I want to build a camera view-frustum to cull my scene.

I am working with column-major matrices in a right-handed coordinate system. (OpenGL style - I'm using C# and Playstation Mobile, but the math should be the same)

I want to get my planes in World-Space, so I build my frustum from the View-Projection Matrix (that's projectionMatrix * viewMatrix). The view Matrix is the inverse of the camera's World-Transform.

The problem is; regardless of what I tweak, I can't seem to get a correct frustum. I think that I may be missing something obvious.

If I "strafe" my camera left or right while still looking down the z-axis, the normals of my planes change so that they are always pointing at the origin of the scene - which makes me think that they are not in world-space...

Oloughlin answered 11/10, 2012 at 9:59 Comment(2)
I was stuck with the same issue yesterday. I have a feeling the OpenGL code might be wrong on that paper, or at least I used the DirectX code and it worked fine, even though I am using OpenGL. I visualized the planes with a point cloud and they look right now. Have you tried that?Dday
I have the same exact issue. I have implemented the algorithm described in the DirectX style paragraph as I am using a row-major matrix query. I was unable to get the correct culling to work. But I have noticed that if the object to be culled is exactly in the world center (0, 0, 0) the culling works perfectly. @Oloughlin were you able to resolve this?Raker
S
15

The planes from a projection matrix can be extracted using the Gribb/Hartmann method as follows, (column major):

void extract_planes_from_projmat(
        const float mat[4][4],
        float left[4], float right[4],
        float bottom[4], float top[4],
        float near[4], float far[4])
{
    for (int i = 4; i--; ) { left[i]   = mat[i][3] + mat[i][0]; }
    for (int i = 4; i--; ) { right[i]  = mat[i][3] - mat[i][0]; }
    for (int i = 4; i--; ) { bottom[i] = mat[i][3] + mat[i][1]; }
    for (int i = 4; i--; ) { top[i]    = mat[i][3] - mat[i][1]; }
    for (int i = 4; i--; ) { near[i]   = mat[i][3] + mat[i][2]; }
    for (int i = 4; i--; ) { far[i]    = mat[i][3] - mat[i][2]; }
}

Where mat4 is the product of the projection matrix and the model-view matrix.

See:

Note: if the matrix components aren't normalized and you require a Hessian Normal Form plane, then you will need to normalize the resulting planes.

Stenography answered 23/1, 2016 at 7:38 Comment(4)
You still need to normalize the planes in case of arbitrary transforms.Blameless
Wouldn't this only be if the planes need to be normalized by whatever code uses them? - Many plane operations don't require this, even so, thanks for the heads up - noted in the answer.Stenography
you are right that you do not need normalized planes for all operations. Though, code can be more robust if you remove some degrees of freedom.Blameless
Sure you might want to, but under some conditions it might be fine not to as well - since normalize can be expensive. Noted in answer that normalizing can be done if its needed.Stenography
B
2

The missing part:

comboMatrix = projection_matrix * Matrix4_Transpose(modelview_matrix)

Then the world-space frustum plane extraction for OpenGL is exactly as mentioned in the Gribb/Hartmann method:

p_planes[0].a = comboMatrix._41 + comboMatrix._11;
p_planes[0].b = comboMatrix._42 + comboMatrix._12;
p_planes[0].c = comboMatrix._43 + comboMatrix._13;
p_planes[0].d = comboMatrix._44 + comboMatrix._14;
// Right clipping plane
p_planes[1].a = comboMatrix._41 - comboMatrix._11;
p_planes[1].b = comboMatrix._42 - comboMatrix._12;
p_planes[1].c = comboMatrix._43 - comboMatrix._13;
p_planes[1].d = comboMatrix._44 - comboMatrix._14;
// Top clipping plane
p_planes[2].a = comboMatrix._41 - comboMatrix._21;
p_planes[2].b = comboMatrix._42 - comboMatrix._22;
p_planes[2].c = comboMatrix._43 - comboMatrix._23;
p_planes[2].d = comboMatrix._44 - comboMatrix._24;
// Bottom clipping plane
p_planes[3].a = comboMatrix._41 + comboMatrix._21;
p_planes[3].b = comboMatrix._42 + comboMatrix._22;
p_planes[3].c = comboMatrix._43 + comboMatrix._23;
p_planes[3].d = comboMatrix._44 + comboMatrix._24;
// Near clipping plane
p_planes[4].a = comboMatrix._41 + comboMatrix._31;
p_planes[4].b = comboMatrix._42 + comboMatrix._32;
p_planes[4].c = comboMatrix._43 + comboMatrix._33;
p_planes[4].d = comboMatrix._44 + comboMatrix._34;
// Far clipping plane
p_planes[5].a = comboMatrix._41 - comboMatrix._31;
p_planes[5].b = comboMatrix._42 - comboMatrix._32;
p_planes[5].c = comboMatrix._43 - comboMatrix._33;
p_planes[5].d = comboMatrix._44 - comboMatrix._34;

These planes now are in world-space and can be used to frustum cull world-space objects.

for(int i = 0; i < 6; i++)
{
    var dist = dot3(world_space_point.xyz, p_planes[i].xyz) + p_planes[i].d + sphere_radius;
    if(dist < 0) return false; // sphere culled
}
Bastinado answered 14/7, 2018 at 3:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.