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

Posted

技术标签:

【中文标题】剔除适用于从视图投影矩阵中提取平面,但不适用于投影矩阵【英文标题】:Culling works with extracting planes from view-projection matrix but not with projection matrix 【发布时间】:2019-05-08 11:56:37 【问题描述】:

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

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

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

这里是相关代码。用“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。您正在改造 AABB 的两个角并根据结果重建一个新的 AABB。 这应该是哪种语言?不要标记两种不同的语言。 @Frank 哦,我没想到这一点.. 相机旋转会使重建的 aabb 无效.. 谢谢,你可以回答。 @toohonestforthissite 它是 C,但我猜也应该同样适用于 C++。这不是特定于 C 的问题。 【参考方案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;

你正在做的是建立一个 AABB,他的 BL 和 TR 是变形的 原 AABB 的 BL 和 TR:

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

你应该做的是改造你原来的 AABB 的所有 8 个角,并围绕它建立一个新的 AABB。但在大多数情况下,使用世界空间剔除平面也绝对没问题。

或者,如果您的问题非常适合边界球,则可以通过使用它来为自己省去很多麻烦。

【讨论】:

谢谢。我一直在为此烦恼,错过了如此微妙但重要的细节。

以上是关于剔除适用于从视图投影矩阵中提取平面,但不适用于投影矩阵的主要内容,如果未能解决你的问题,请参考以下文章

视锥体剔除(Frustum Culling)算法详解-透视投影矩阵直接推导

视锥体剔除(Frustum Culling)算法详解-透视投影矩阵直接推导

视锥体剔除(Frustum Culling)算法详解-透视投影矩阵直接推导

相机矩阵(Camera Matrix)

如何从 WinRT Windows.Media API 中获取 Hololens 可定位相机视图和投影矩阵

如何将 3D 协方差矩阵投影到给定的图像平面(姿势)