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

Culling works with extracting planes from view-projection matrix but not with projection matrix

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

文章提到,如果矩阵是投影矩阵,那么平面将在视图中-space。所以我需要转换我的 AABB 坐标以查看 space 来进行剔除测试。但是,这不起作用。
但是,如果从视图投影矩阵中提取平面并在模型 space 中使用 AABB 坐标进行测试,一切正常。

我所做的唯一更改是使用每个相机运动的视图投影矩阵更新截锥平面,并将 AABB 坐标转换为模型 space 而不是视图 space。

这是相关代码。用 "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
}

看起来它剔除太多了。另请注意:这种异常剔除仅在我查看正 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
}

完美

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

转换 AABB 究竟意味着什么?

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

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

这代表 space 的实际区域显然看起来像钻石:

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

但是,由于我们想将其编码为 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。但在大多数情况下,使用 world-space 剔除平面也绝对没问题。

或者,如果您的问题本身很适合边界球体,您可以使用它来省去很多麻烦。