剔除适用于从视图投影矩阵提取平面,但不适用于投影矩阵

时间:2018-12-06 18:06:17

标签: c opengl frustum culling

我已经使用this文章中介绍的平面提取方法实现了平截头体剔除。

文章提到,如果矩阵是投影矩阵,则平面将位于视图空间中。因此,我需要转换AABB坐标以查看空间以进行剔除测试。但是,这不起作用。
但是,如果从视图投影矩阵中提取平面并在模型空间中使用AABB坐标进行测试,则一切正常。

我所做的唯一更改是在每次相机运动时使用视图投影矩阵更新视锥平面,并将ABB坐标转换为模型空间而不是视图空间。

这是相关的代码。用“ diff”注释的行是两个版本之间的唯一更改。

基于投影矩阵的视锥剔除的代码:

// called only at initialization
void camera_set_proj_matrix(camera *c, mat4 *proj_matrix)
{
    c->proj_matrix = *proj_matrix;
    // diff
    extract_frustum_planes(&c->frustum_planes, &c->proj_matrix); 
}

void camera_update_view_matrix(camera *c)
{
    mat4_init_look(&c->view_matrix, &c->pos, &c->dir, &VEC3_UNIT_Y);
    mat4_mul(&c->vp_matrix, &c->proj_matrix, &c->view_matrix);
    // diff
}

void chunk_render(const chunk *c, chunk_pos pos, const camera *camera, GLuint mvp_matrix_location)
{
    mat4 model_matrix;
    block_pos bp = chunk_pos_to_block_pos(pos);
    mat4_init_translation(&model_matrix, bp.x, bp.y, bp.z);
    mat4 mv_matrix;
    mat4_mul(&mv_matrix, &camera->view_matrix, &model_matrix);

    vec4 min = {0, 0, 0, 1};
    vec4 max = {CHUNK_SIDE, CHUNK_HEIGHT, CHUNK_SIDE, 1};
    // diff: using model view matrix here
    mat4_mul_vec4(&min, &mv_matrix, &min); 
    // diff: using model view matrix here
    mat4_mul_vec4(&max, &mv_matrix, &max); 
    AABB aabb = {{min.x, min.y, min.z}, {max.x, max.y, max.z}};
    if (AABB_outside_frustum(&aabb, &camera->frustum_planes)) return;

    //draw
}

Result 似乎淘汰的太多了。另请注意:这种异常剔除仅在我正向z方向观察时发生。

基于视图投影的剔除代码:

void camera_set_proj_matrix(camera *c, mat4 *proj_matrix)
{
    c->proj_matrix = *proj_matrix; 
    // diff
}

void camera_update_view_matrix(camera *c)
{
    mat4_init_look(&c->view_matrix, &c->pos, &c->dir, &VEC3_UNIT_Y);
    mat4_mul(&c->vp_matrix, &c->proj_matrix, &c->view_matrix);
    // diff: update frustum planes based on view projection matrix now
    extract_frustum_planes(&c->frustum_planes, &c->vp_matrix);
}

void chunk_render(const chunk *c, chunk_pos pos, const camera *camera, GLuint mvp_matrix_location)
{
    mat4 model_matrix;
    block_pos bp = chunk_pos_to_block_pos(pos);
    mat4_init_translation(&model_matrix, bp.x, bp.y, bp.z);
    mat4 mv_matrix;
    mat4_mul(&mv_matrix, &camera->view_matrix, &model_matrix);

    vec4 min = {0, 0, 0, 1};
    vec4 max = {CHUNK_SIDE, CHUNK_HEIGHT, CHUNK_SIDE, 1};
    // diff: using model matrix now
    mat4_mul_vec4(&min, &model_matrix, &min); 
    // diff: using model matrix now
    mat4_mul_vec4(&max, &model_matrix, &max); 
    AABB aabb = {{min.x, min.y, min.z}, {max.x, max.y, max.z}};
    if (AABB_outside_frustum(&aabb, &camera->frustum_planes)) return;

    // draw
}

enter image description here 完美

我不知道为什么当我正确转换aabb时,仅投影方法会以一种奇怪的方式起作用:/

1 个答案:

答案 0 :(得分:1)

转换AABB到底意味着什么?

让我们看一下2D问题。假设我有一个2D AABB(由左下角和右上角定义),我想将其旋转45度。

---------
|       |
|       |   ->   ???
|       |
---------

此代表的实际空间区域显然看起来像钻石:

    / \
  /     \
/         \
\         /
  \     /
    \ /

但是,由于我们要将其编码为AABB,因此生成的AABB必须看起来像这样:

-------------
|    / \    |
|  /     \  |
|/         \|
|\         /|
|  \     /  |
|    \ /    |
-------------

但是,请查看您的代码:

mat4_mul_vec4(&min, &model_matrix, &min); 
// diff: using model matrix now
mat4_mul_vec4(&max, &model_matrix, &max); 
AABB aabb = {{min.x, min.y, min.z}, {max.x, max.y, max.z}};

您正在做的是建立一个由BL和TR转换而成的AABB 原始AABB的BL和TR:

    / \    
  /     \  
/         \
-----------
\         /
  \     /  
    \ /    

您应该做的是转换原始AABB的所有8个角,然后围绕它建立一个新的AABB。但是在大多数情况下,使用世界空间剔除飞机也绝对可以。

或者,如果您的问题很适合边界球,那么使用它可以为自己省很多麻烦。