【问题标题】:Three.js detect when object is partially and fully occludedThree.js 检测对象何时被部分和完全遮挡
【发布时间】:2020-05-15 15:23:16
【问题描述】:

我正在尝试检测 Three.js 中的对象何时被部分和完全遮挡(隐藏在)另一个对象。

我当前的简单解决方案将单条射线投射到对象的中心:

function getScreenPos(object) {
  var pos = object.position.clone();
  camera.updateMatrixWorld();
  pos.project(camera);
  return new THREE.Vector2(pos.x, pos.y);
}

function isOccluded(object) {
  raycaster.setFromCamera(getScreenPos(object), camera);
  var intersects = raycaster.intersectObjects(scene.children);
  if (intersects[0] && intersects[0].object === object) {
    return false;
  } else {
    return true;
  }
}

但它不考虑对象的尺寸(宽度、高度、深度)。

不被遮挡(因为物体的中心不在后面)

被遮挡(因为物体的中心在后面)

查看工作演示:

https://jsfiddle.net/kmturley/nb9f5gho/57/

目前认为我可以计算对象框的大小,并为框的每个角投射光线。但这可能还是有点太简单了:

var box = new THREE.Box3().setFromObject(object);
var size = box.getSize();

我想找到一种更强大的方法,它可以提供 partially occludedfully occluded 布尔值,甚至可能是 percentage occluded

【问题讨论】:

    标签: javascript three.js 3d occlusion occlusion-culling


    【解决方案1】:

    搜索 Stack Overflow 和有关“GPU 选择”的 Three.js 示例。这个概念可以分为三个基本步骤:

    1. 将每个形状的材质更改为独特的平面 (MeshBasicMaterial) 颜色。
    2. 使用独特的材质渲染场景。
    3. 读取渲染帧的像素以收集颜色信息。

    您的方案允许您提出一些警告。

    1. 只为您正在测试的形状提供独特的颜色 - 其他一切都可以是黑色。
    2. 您不需要渲染整个场景来测试一个形状。您可以调整视口以仅渲染相关形状周围的区域。
    3. 因为您只为您的测试部分指定了一种颜色,所以其余数据应该为零,从而更容易找到与您的独特颜色匹配的像素。

    现在您有了像素数据,您可以确定以下内容:

    • 如果没有像素匹配唯一的颜色,则形状被完全遮挡。
    • 如果某些像素与唯一颜色匹配,则该形状至少部分可见。

    第二个项目符号表示该形状“至少部分”可见。这是因为您无法使用当前拥有的信息测试完全可见性。

    会做的(其他人可能有更好的解决方案)是第二次渲染相同的视口,但只有测试形状可见,即相当于零件完全可见。有了这些信息,将像素与第一次渲染进行比较。如果两者具有相同数量(可能在容差范围内)的唯一颜色像素,那么您可以说该部分是完全可见/未遮挡的。

    【讨论】:

    【解决方案2】:

    根据 TheJim01 的回答,我设法获得了 WebGL1 的工作版本!

    首先创建第二个更简单的场景用于计算:

    pickingScene = new THREE.Scene();
    pickingTextureOcclusion = new THREE.WebGLRenderTarget(window.innerWidth / 2, window.innerHeight / 2);
    pickingMaterial = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors });
    pickingScene.add(new THREE.Mesh(BufferGeometryUtils.mergeBufferGeometries([
      createBuffer(geometry, mesh),
      createBuffer(geometry2, mesh2)
    ]), pickingMaterial));
    

    将您的对象重新创建为 Buffer Geometry(性能更快):

    function createBuffer(geometry, mesh) {
      var buffer = new THREE.SphereBufferGeometry(geometry.parameters.radius, geometry.parameters.widthSegments, geometry.parameters.heightSegments);
      quaternion.setFromEuler(mesh.rotation);
      matrix.compose(mesh.position, quaternion, mesh.scale);
      buffer.applyMatrix4(matrix);
      applyVertexColors(buffer, color.setHex(mesh.name));
      return buffer;
    }
    

    根据 mesh.name 添加颜色,例如id 1、2、3 等

    function applyVertexColors(geometry, color) {
      var position = geometry.attributes.position;
      var colors = [];
      for (var i = 0; i < position.count; i ++) {
        colors.push(color.r, color.g, color.b);
      }
      geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
    }
    

    然后在渲染循环期间检查该纹理的第二个场景,并将像素数据与网格名称匹配:

    function isOccludedBuffer(object) {
      renderer.setRenderTarget(pickingTextureOcclusion);
      renderer.render(pickingScene, camera);
      var pixelBuffer = new Uint8Array(window.innerWidth * window.innerHeight);
      renderer.readRenderTargetPixels(pickingTextureOcclusion, 0, 0, window.innerWidth / 2, window.innerHeight / 2, pixelBuffer);
      renderer.setRenderTarget(null);
      return !pixelBuffer.includes(object.name);
    }
    

    您可以在此处查看 WebGL1 工作演示:

    https://jsfiddle.net/kmturley/nb9f5gho/62/

    使用这种方法需要注意的一点是,您的采摘场景需要与主场景的变化保持同步。因此,如果您的对象移动位置/旋转等,它们也需要在拾取场景中更新。在我的示例中,相机正在移动,而不是物体,因此它不需要更新。

    对于WebGL2我们会有更好的解决方案:

    https://tsherif.github.io/webgl2examples/occlusion.html

    但这并不是所有浏览器都支持的:

    https://www.caniuse.com/#search=webgl

    【讨论】:

    • 感谢您花时间制作这两个演示!真的很有帮助
    • 没问题!打算在某个时候写一篇博客文章:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    相关资源
    最近更新 更多