Babylon.js关于遮挡检测的问题

Babylon.js关于遮挡检测的问题

1. API和参数

  • 我使用的API是Mesh.isOccluded
  • Mesh.occlusionType = BABYLON.AbstractMesh.OCCLUSION_TYPE_STRICT
  • Mesh.occlusionQueryAlgorithmType = BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE

2. 场景

  • 我创建了一个盒子,并在盒子的表面贴满了贴片

    img

  • 在实际测试时,我的盒子是不透明状态。

3. 问题

  • 我对盒子表面的贴片进行了遮挡检测
  • 在进行检测之前,多次使用Mesh.isOcclusionQueryInProgress来判断系统是否对该Mesh的遮挡进行了计算
  • 然而,盒子在正常视角下最多只会裸露三个面,也就是说,我的贴片应该最多只有一半是可见的,然而,实际测试中发现,可见的贴片始终在半数以上,有的甚至全部可见

4. 相关代码

  •     // 创建贴片
        const plane = new BABYLON.MeshBuilder.CreateBox("p"+i+j, {width:1, height:1, depth:0.01}, scene);
        plane.occlusionQueryAlgorithmType = BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
        plane.occlusionType = BABYLON.AbstractMesh.OCCLUSION_TYPE_STRICT;
    
  •       // 判断系统是否对mesh的遮挡进行了计算
          // 代码中patches是plane的数组
            let ready_counter = 0;
            function ifOccludedDataReady(box){
              let patches = box.patches;
              let if_ready = true;
    
              if(ready_counter<1){
                for(let i=0;i<6;i++){
                  for(let j=0;j<patches[i].length;j++){
                    if_ready = if_ready && patches[i][j].isOcclusionQueryInProgress;
                    patches[i][j].isOccluded; // weakup
                    if(!if_ready){
                      return false;
                      }
                   }
                }
                ready_counter++;
                return false;
            }
           else{
              ready_counter = 0;
              return true;
           }
         }
    

5. 结果

  • 可以看到,虽然box只裸露了三个表面,但是,所有的plane都处于未被遮挡状态。
  • img

    img

    img

6. 或者我们有没有其他的方法来检测遮挡?

引用 皆我百晓生 小程序回复内容作答:
除了使用Mesh.isOccluded方法外,Babylon.js还提供了其他的方法来检测遮挡。

一种方法是使用场景的可见性查询(Scene.visibilityQuery)。你可以通过设置Mesh.overrideMaterial,将盒子的材质替换为一个透明材质,并将所有贴片的可见性设置为false。然后,在每次渲染循环中,使用Scene.visibilityQuery来判断贴片是否可见。

另一种方法是使用遮挡查询(OcclusionQuery)。首先,创建一个OcclusionQueryRenderer对象,将盒子和贴片添加到该对象中。然后,通过OcclusionQueryRenderer.isOcclusionVisible方法来判断贴片是否被盒子遮挡。

这些方法可能需要一些额外的代码和计算,但它们可能会提供更准确的遮挡检测结果。

【以下回答由 GPT 生成】

答案:

  1. 参数Mesh.occlusionType是用来指定遮挡检测类型的。 OCCLUSION_TYPE_STRICT表示使用严格的遮挡检测算法,即只有在场景中完全遮挡住一个物体时才会返回true。OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE表示使用保守的遮挡检测算法,这种算法可能会返回假阳性,即在实际上物体并没有完全遮挡住的情况下也会返回true。

  2. 可能导致遮挡检测结果不符合预期的原因有以下几点:

  3. 盒子的表面贴片可能与相机视角平行,无法进行遮挡检测。因为根据遮挡检测的原理,贴片只有在场景中被其他物体部分或完全遮挡住时才会返回true。
  4. 使用的遮挡检测算法不适用于盒子的表面贴片,导致遮挡检测结果不准确。可能需要尝试其他的遮挡检测算法来解决这个问题。

  5. 以上代码可能导致遮挡检测结果不准确的问题有以下几点:

  6. ifOccludedDataReady函数中的代码在第一次调用时会返回false,因为ready_counter初始值为0,所以条件ready_counter<1为true。这会导致第一次调用遮挡检测结果始终为false,而不进行实际的遮挡检测计算。
  7. 在该函数中,对于每个贴片,只要有一个贴片的遮挡检测结果为false,就会立即返回false。这可能导致贴片的遮挡检测结果不准确,因为只检测了每个贴片的遮挡检测结果,而没有考虑整个盒子表面的遮挡情况。

解决方案:

  1. 根据问题描述,使用Mesh.isOccluded方法检测物体是否被遮挡的代码是正确的,参数的含义也正确地设置了严格模式的遮挡检测算法。所以第一个问题已经解决。

  2. 贴片遮挡检测结果不符合预期的问题可能是由于以下原因导致的:

  3. 与相机视角平行的贴片无法进行遮挡检测,请确保贴片在场景中被其他物体部分或完全遮挡住。
  4. Mesh.occlusionQueryAlgorithmType参数可能不适用于盒子的表面贴片,尝试使用其他的遮挡检测算法,如BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_STRICT

  5. 修正ifOccludedDataReady函数中的问题:

function ifOccludedDataReady(box) {
  let patches = box.patches;
  let if_ready = true;

  if (ready_counter < 1) {
    for (let i = 0; i < 6; i++) {
      for (let j = 0; j < patches[i].length; j++) {
        if_ready = if_ready && patches[i][j].isOcclusionQueryInProgress;
        patches[i][j].isOccluded; // weakup
        if (!if_ready) {
          return false;
        }
      }
    }
    ready_counter++;
    return if_ready; // 修改返回逻辑
  } else {
    ready_counter = 0;
    return true;
  }
}

通过以上修改,ifOccludedDataReady函数将在所有贴片的遮挡检测结果都准备完毕时才返回true。

希望以上解决方案能够解决你的问题。如果还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^