Trimming point cloud with BoxGeometry as reference

I am using PCDLoader to load my pointcloud. I also have several meshes with BoxGeometry to be used as reference on trimming the pointcloud. Specifically any points outside the Box needs to be removed. I’ve used clippingPlanes for 1 cuboid but I don’t think it’ll work with multiple cuboids, or at least I could not get it to work.

Following is what I used for single cuboid.

  const [vertex0, vertex1, vertex2, vertex3, vertex4, vertex5, vertex6] = getVerticesFromCuboid(editingCuboid)
  const plane1 = new Plane().setFromCoplanarPoints(vertex2, vertex1, vertex0)
  const plane2 = new Plane().setFromCoplanarPoints(vertex0, vertex1, vertex4)
  const plane3 = new Plane().setFromCoplanarPoints(vertex1, vertex2, vertex5)
  const plane4 = new Plane().setFromCoplanarPoints(vertex2, vertex3, vertex6)
  const plane5 = new Plane().setFromCoplanarPoints(vertex4, vertex3, vertex0)
  const plane6 = new Plane().setFromCoplanarPoints(vertex4, vertex5, vertex6)

material.clippingPlanes = [plane1, plane2, plane3, plane4, plane5, plane6]

Any tips on how to get it working with multiple cuboids? Preferably non-destructive (opacity 0 the outer points?) since the cuboids can be modified.

You can modify PointsMaterial and pass information about boxes in uniforms, or in DataTexture - creativity is up to you.

non-modified PointsMaterial:

modified PointsMaterial:
change color, if a point is inside a box

trimmed by boxes

4 Likes

@prisoner849 Thanks for the pointers! I managed to get it working with uniforms and shaders but for some reason the rotation is a little off and I couldn’t figure out why.


The yellow lines is the mask rendered with box3helper and as you can see each of them is slightly rotated from the actual box. The box is correct but the affected points are not.

Some pointers on why this is happening would be grateful!

onBeforeCompile

const uniformsData = maskRegions.filter((region) => !region.invisible).map(generateCuboidUniforms)

const uniforms = {
  maskRegion: { value: uniformsData },
}

shader.uniforms = { ...(shader.uniforms || {}), ...uniforms }

shader.vertexShader = shader.vertexShader.replace(
  `void main() {`,
  ` 
    varying vec4 fragPosition;
  
    void main() {
        fragPosition = modelMatrix * vec4(position, 1.0);
    `
)

shader.fragmentShader = shader.fragmentShader
  .replace(
    `void main() {`,
    `
        struct Region {
          mat4 modelMatrix;
          vec3 min;
          vec3 max;
        };
  
        uniform Region maskRegion[${uniformsData.length}];
        varying vec4 fragPosition;
  
        bool checkWithin(Region region)
        {
          // we need fragment position localized to the mask region
          vec4 localPos = region.modelMatrix * fragPosition;
      
          // Check if the fragment is inside the cube in its local space
          return all(greaterThanEqual(localPos.xyz, region.min)) && all(lessThanEqual(localPos.xyz, region.max));
        }
  
        void main() {
          bool toDiscard = ${shouldNotDiscard ? 'false' : 'true'};
          float defaultOpacity = opacity;
          float updatedOpacity = ${shouldNotDiscard ? '0.05' : '0.0'};
  
          for(int i=0; i<${uniformsData.length}; i++)
          {
            if (checkWithin(maskRegion[i]))
            {
              updatedOpacity = defaultOpacity;
              toDiscard = false;
            }
          }

          if (toDiscard) {
            discard;
            return;
          }
      `
  )
  .replace(
    'vec4 diffuseColor = vec4( diffuse, opacity );',
    'vec4 diffuseColor = vec4( diffuse, updatedOpacity );'
  )
export const generateCuboidUniforms = (cuboid: Cuboid) => {
  const { center, rotation, extent } = cuboid

  const boundingBox = new Box3(
    new Vector3(-extent[0] / 2, -extent[1] / 2, -extent[2] / 2),
    new Vector3(extent[0] / 2, extent[1] / 2, extent[2] / 2)
  )

  // Calculate the model matrix
  const modelMatrix = new Matrix4()
  modelMatrix
    .compose(
      new Vector3(...center),
      new Quaternion().setFromRotationMatrix(new Matrix4().setFromMatrix3(new Matrix3().fromArray(rotation))),
      new Vector3(1, 1, 1)
    )
    .invert()

  return {
    modelMatrix,
    min: boundingBox.min,
    max: boundingBox.max,
  }
}

Maybe I overlooked something :thinking:
Do you apply matrices to box helpers somewhere in your code?

It’s basically the same thing, except with the non-inverted modelMatrix being applied on the box3.

const { center, rotation, extent } = cuboid

const modelMatrix = new Matrix4().compose(
  new Vector3(...center),
  new Quaternion().setFromRotationMatrix(new Matrix4().setFromMatrix3(new Matrix3().fromArray(rotation))),
  new Vector3(1, 1, 1)
)

const boundingBox = new Box3(
  new Vector3(-extent[0] / 2, -extent[1] / 2, -extent[2] / 2),
  new Vector3(extent[0] / 2, extent[1] / 2, extent[2] / 2)
).applyMatrix4(modelMatrix)

.applyMatrix4() on Box3 will give you another AABB.
That’s how it works under the hood: three.js/src/math/Box3.js at 09fe0527a9aa5aaca7aaf17531e6c0d0efaa8c59 · mrdoob/three.js · GitHub

1 Like

You need to apply that matrix to Box3Helper, something like this: Edit fiddle - JSFiddle - Code Playground

2 Likes