首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >果蝇扑杀虫

果蝇扑杀虫
EN

Stack Overflow用户
提问于 2021-01-09 01:07:53
回答 2查看 435关注 0票数 1

所以我在我的游戏引擎中实现了Frustum筛选,并且我遇到了一个奇怪的bug。我正在绘制一座被分割成块状的建筑,我只呈现在果果中的块。我的相机从四周开始(-.033,11.65,2.2),一切看起来都很好。我开始四处走动,没有闪烁的迹象。当我在结果剔除代码中设置一个断点时,我可以看到它确实是在剔除一些网格。一切看起来都很棒。然后当我到达大楼的中心,周围(3.9,4.17,2.23)的网格开始消失。在另一边也是如此。我不知道为什么这个bug会存在。

我通过使用这里列出的提取方法视图曲面的提取(Gribb & Hartmann方法)来实现水果的剔除。我不得不使用glm::反向(),而不是像它所建议的那样进行转置,我认为矩阵的数学是针对行主矩阵的,所以我改变了它。总之,我的平面计算看起来就像

代码语言:javascript
复制
std::vector<Mesh*> render_meshes;
auto comboMatrix = proj * glm::inverse(view * model);
glm::vec4 p_planes[6];

p_planes[0] = comboMatrix[3] + comboMatrix[0]; //left
p_planes[1] = comboMatrix[3] - comboMatrix[0]; //right
p_planes[2] = comboMatrix[3] + comboMatrix[1]; //bottom
p_planes[3] = comboMatrix[3] - comboMatrix[1]; //top
p_planes[4] = comboMatrix[3] + comboMatrix[2]; //near
p_planes[5] = comboMatrix[3] - comboMatrix[2]; //far

for (int i = 0; i < 6; i++){
    p_planes[i] = glm::normalize(p_planes[i]);
}
for (auto mesh : meshes) {
    if (!frustum_cull(mesh, p_planes)) {
        render_meshes.emplace_back(mesh);
    }
}

然后,我决定根据每个网格的边界框(由ASSIMP使用aiProcess_GenBoundingBoxes标志计算)进行裁剪,如下所示(返回正确的剔除方法)

代码语言:javascript
复制
glm::vec3 vmin, vmax;
for (int i = 0; i < 6; i++) {
    // X axis
    if (p_planes[i].x > 0) {
        vmin.x = m->getBBoxMin().x;
        vmax.x = m->getBBoxMax().x;
    }
    else {
        vmin.x = m->getBBoxMax().x;
        vmax.x = m->getBBoxMin().x;
    }
    // Y axis
    if (p_planes[i].y > 0) {
        vmin.y = m->getBBoxMin().y;
        vmax.y = m->getBBoxMax().y;
    }
    else {
        vmin.y = m->getBBoxMax().y;
        vmax.y = m->getBBoxMin().y;
    }
    // Z axis
    if (p_planes[i].z > 0) {
        vmin.z = m->getBBoxMin().z;
        vmax.z = m->getBBoxMax().z;
    }
    else {
        vmin.z = m->getBBoxMax().z;
        vmax.z = m->getBBoxMin().z;
    }
    if (glm::dot(glm::vec3(p_planes[i]), vmin) + p_planes[i][3] > 0)
        return true;
    
}
return false;

有指引吗?

更新1:规范化表示平面的完整vec4是不正确的,因为只有vec3表示平面的正常。此外,对于这个实例,规范化并不是必要的,因为我们只关心距离的标志(而不是大小)。

还需要注意的是,我应该使用矩阵的行,而不是列。我正在通过替换

代码语言:javascript
复制
p_planes[0] = comboMatrix[3] + comboMatrix[0];

使用

代码语言:javascript
复制
p_planes[0] = glm::row(comboMatrix, 3) + glm::row(comboMatrix, 0);

在任何情况下。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-01-09 07:24:34

在遵循@derhass解决方案为交叉测试正确规范平面之后,您将执行以下操作

对于包围盒平面交点,在把你的盒子投影到我们称为p的平面上,在计算了盒子的中点之后,比如说m,在计算了那个中点到平面的距离后,让d检查交叉点。

代码语言:javascript
复制
  d<=p

但是对于水果剔除,我们只是不希望我们的盒子不与我们的果肉平面相交,但我们希望它与我们的平面相交,只有在那时我们才能确定我们的盒子中没有任何部分与我们的平面相交。

代码语言:javascript
复制
    if(d<=-p)//then our box is fully not intersecting our plane so we don't draw it or cull it[d will be negative if the midpoint lies on the other side of our plane]

同样,对于三角形,我们检查三角形的所有三个点与平面的距离是否为负数。

要将一个盒子投射到平面上,我们取盒子的3轴,y,z单位向量,按盒子的半宽、高度、深度进行缩放,并找出每个点productsTake的和--只有每一个点积的正大小--没有符号距离--这将是你的'p‘。

对于AABB来说,不采用上述方法,您也可以使用相同的方法对OOBB进行筛选,因为只有轴会发生变化。

编辑:如何将包围盒投射到飞机上?

让我们考虑一个AABB作为我们的例子,它有以下参数

代码语言:javascript
复制
Lower extent Min(x,y,z)
Upper extent Max(x,y,z)
Up Vector      U=(0,1,0)
Left Vector.   L=(1,0,0)
Front Vector.  F=(0,0,1)

步骤1:计算半尺寸

代码语言:javascript
复制
half_width=(Max.x-Min.x)/2;
half_height=(Max.y-Min.y)/2;
half_depth=(Max.z-Min.z)/2;

第二步:将盒子的每个单独的轴投影到平面法线上,只取每一个点积的正量值按每一个半维的大小,然后求出总和。确保盒轴和平面法线都是单位向量。

代码语言:javascript
复制
float p=(abs(dot(L,N))*half_width)+
        (abs(dot(U,N))*half_height)+
        (abs(dot(F,N))*half_depth);

 abs() returns absolute magnitude we want it to be positive 
 because we are dealing with distances

其中N是平面的法线单位向量

第三步:计算盒中点

代码语言:javascript
复制
 M=(Min+Max)/2;

第四步:计算中点距平面的距离

代码语言:javascript
复制
d=dot(M,N)+plane.w

第5步:检查

代码语言:javascript
复制
d<=-p //return true i.e don't render or do culling

U可以看到如何将his用于OOBB,其中U,F,L向量是OOBB的轴,中心(中点)是你手动传递的参数。

对于一个球体,你可以计算球心距平面(称为d)的距离,但是要做检查。

代码语言:javascript
复制
  d<=-r //radius of the sphere

将其放入一个名为Put (平面,界)的函数中,如果边界完全在平面之外,则返回true,然后对6个平面中的每一个都返回true。

代码语言:javascript
复制
   bool is_inside_frustum()
  {
    for(Plane plane:frustum_planes)
   {
      if(outside(plane,AABB))
     { 
       return false
     }
    }
    return true;
  } 
票数 1
EN

Stack Overflow用户

发布于 2021-01-09 14:43:28

你使用GLM不正确。根据格里布和哈特曼纸,可以提取平面方程作为矩阵不同行的和或差,但在glm中,mat4 foo; foo[n]将产生第n列(类似于GLSL的设计)。

这个在这里

对于(int i= 0;i< 6;i++){ p_planesi =glm::正常化(P_planesi);}

也没有意义,因为glm::normalize(vec4)将简单地标准化一个四维向量。这将导致飞机沿其法线方向移动。只有xyz组件必须被带入单位长度,并且必须相应地缩放w。文件本身甚至对此作了详细说明。然而,既然你只需要知道一个点在哪个半空间上,将平面方程归一化是对循环的浪费,那么你只关心符号,而不是值的魔力。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65638359

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档