Capping clipped planes using stencil on a BufferGeometry()

So, is the naming of all these items necessary as I’ve been doing?

Or, if I reverted and kept them as-is, and just designate iterations of their render order enough?

Will the animate() function know which it’s supposed to be controlling via the gui

I’m not sure what you’re referring to? Is there a post or fiddle I missed that these questions are relevant to?

Just the first post you replied to of mine.

I’ll try the rendering order.
no fiddle / codepen as of yet.
if i make some progress / get stumped further after this attempt i’ll try to make one.

1 Like

so at :
clippedColorFront.renderOrder = 6;
on line 189

it’s equal to 6.
are you saying then the “6” (above) be “2” then “4” ?

and the line 166’s po.renderOrder = i + 1.1; be 1.1, then 3.1?
(unsure about how the math will work right there with the .1)

and have this pattern continue for any following objects?

just trying to be clear. thnx

The important thing is that all relevant stencil passes happen together and that the clip cap rendering happen last in those sets of stencil rendering. That demo has 3 clip caps that have to get rendered and in this order:

  • Clip Plane Surface 1
    • 4x 2x stencil passes with render order 1
    • 1x clip surface as with render order 1.1
  • Clip Plane Surface 2
    • 4x 2x stencil passes with render order 2
    • 1x clip surface as with render order 2.1
  • Clip Plane Surface 3
    • 4x 2x stencil passes with render order 3
    • 1x clip surface as with render order 3.1
  • Geometry surface color
    • 1x geometry rendered with render order 6

After all the stencil passes we render the normal surface of the geometry. TBH the fact that that is rendered with render order 6 is not so important because it does not do anything with the stencil buffer. The geometry surface material is only rendered once though.

1 Like

Ok … Very good info sir.
I’m just seeing now, at

line 141
Where
var stencilGroup = createPlaneStencilGroup( geometry, plane, i + 1 );

Should have the “1” be also iterated like you suggest.
Correct?

Should have the “1” be also iterated like you suggest.

I’m not sure I follow what you mean. That line adds the stencil material meshes with render order i + 1.

Also I’ve looked more closely at that example again and it looks like there are actually only two stencil passes rather than four like I wrote before. There are other approaches that require four stencil passes but it doesn’t look like that’s what I’d used in this case. Everything else still applies though.

1 Like

Ok, so,
Considering that, would you mind adjusting your last comment before this according to what you just retracted?

Just for clarity, posterity etc

Thanks so much for the help.

yup… THIS … is exactly what made for absolute success with this issue…

line 141
Where
var stencilGroup = createPlaneStencilGroup( geometry, plane, i + 1 );

Having the “1” be also iterated like you suggest.

i just had a var “Object_Number” be raised 1 each time through the code, having…

var stencilGroup = createPlaneStencilGroup(mesh.geometry, plane, i + 1 + Object_Number);

po.renderOrder = (i + 1.1) + Object_Number;

respectively. and it works…

thank you so much for your advice on things.

i’ll def be back soon with something else hah!

EDIT: var “Object_Number” was treated as such…
for (var i_index in LIST_Objects_In_loaded_SCENE) { i = LIST_Objects_In_loaded_SCENE[i_index]; if (Object_Number == 0) { Object_Number = 1; } else { Object_Number = Object_Number + 3;
:+1:

Excellent! Thanks so much :slight_smile:

@gkjohnson Thanks for the response. One last question :slight_smile: - How would I modify the stencils incase of rotation of the clipping plane about an axis by a certain angle? I tried

var oldPlaneNormal = plane.normal.clone();
plane.normal.applyAxisAngle(rotAxis, changeAngle);
planeMesh.quaternion.setFromUnitVectors(oldPlaneNormal, plane.normal);

This successfully rotates the clipping plane but does not cap the hollow areas properly as seen in the fiddle ( I also recreated the stencils to the scene but to no avail):
https://jsfiddle.net/czm2nLa4/

Nevermind. I figured it out. I just had to do this instead:

planeMesh.quaternion.setFromUnitVectors(forwardVector, plane.normal); :slight_smile:

Just reading this now.
I’m doing the exact same thing.
Radial pie-chart style cut away reveals.

I’ll try your code out to try to fix this nasty gimble lock I have on it.

EDIT: could you update your fiddle with that?
(when the heck will fiddle get a usable mobile mode?? grr. :anguished:

This might be a possible solution:

The plane and the planemeshes need to be modified properly so their normals match. Hope this helps!

I should really post this as a separate topic but it’s too small a problem (either that or I am too much of a novice to not know how small or big it is) :slight_smile: . Why does THREE.Raycaster.intersectObjects() not work on the mesh for the cap ? I noticed that the geometry is actually the geometry given by the combination of the stencil and the plane but I need mouse intersection on the capped surfaces only.

Figured it! Never thought it made sense but this was the easiest solution I could think of: Use raycaster.intersectObjects() on the mesh that is being clipped itself and find the number of intersections. I then filter all intersections that lie on the unclipped side of the plane (as intersectObjects() interacts even with the invisible clipped part). I did this by checking the signed distance of the points where the intersections were found with the clipped plane and used the sign of this distance to check if it was on the clipped side or not. From the filtered intersections I then did this:

If the no. of intersections filtered is Odd, the clicked area on the screen is the cap itself and if it is even then it is somewhere outside the cap. This holds true only if the mesh material has the property side = THREE.DoubleSide. This makes sense now since a ray has to come out of every mesh it enters in a double sided mesh :slight_smile:

You can also give each plane mesh part that is generated a name, and name the group they are added to. Then traverse the group for each descendant to do your ray casting on.

Im referring to the po. Objects and the mesh0 mesh1 objects that are in the for loops.

1 Like

I know I come to this late however I have done something similar to create a cross selection. I use CSG to create the cross section with the library https://github.com/oathihs/ThreeCSG.

1 Like

Hey Thomas,
I had looked into CSG before, to achieve this effect. Do you think you could possibly share some of your code you’ve made using it in an example?
I’m very interested to see how it’s implemented!
Looking at the github, and it’s example demo I’m wondering if it provides capped ends to the geometry at the intersections.
Thanks!

I have similar question. Unfortunaly I cant figure out how to apply this technique to gltf model. This is my code:
clipping gltf
I merge all gltf geometries into one and trying to cap the “floor”.
p.s. updated link with fixed stencil plane rotation

Hello @GlifTek i am continuously trying to achieve the capping feature over the combination of three different geometry entities using this example: three.js examples

This example uses three sides clipping planes for a single geometry whereas in my project i have 5 planes cutting the geometry (combination of three geometries ) from 5 different sides.

I tried to acheive everything as described in the example and able to cut the geometry as required but the cap that is applied is only visible when you rotate the model over a certain area of the scene and if i try to zoom in and out or use pan it just the cropped area gets transparent or void spaces visualization over the capped surface.
I am pretty much sure it has something the do with the render order . Do you have any ideas on this or what specific area i should look into.
Below is the code that i am currently using.

document.getElementById("Clip").onclick = function (e) {

  //filles global plane array with planes
  createPlanesusingPts();
  var stencilGroup = null;
  var poGroup = null;
  var planeGeom = null;
  for (let j = 0; j < 5; j++) {
    let pplane = Planearray[j];
    planeGeom = new THREE.PlaneGeometry(200, 200);
    for (let i = 0; i < group.children.length; i++) {
    // group.children.length has 3 different mesh object

      const poGroup = new THREE.Group();
      const stencilGroup = createPlaneStencilGroup(
        group.children[i].geometry,
        pplane,
        j + 1
      );
    }

      // add the color
    const planeMat = new THREE.MeshStandardMaterial({
        color: 0xe91e63,
        metalness: 0.1,
        roughness: 0.75,
        clippingPlanes: Planearray.filter((p) => p !== pplane),
        stencilWrite: true,
        stencilRef: 0,
        stencilFunc: THREE.NotEqualStencilFunc,
        stencilFail: THREE.ReplaceStencilOp,
        stencilZFail: THREE.ReplaceStencilOp,
        stencilZPass: THREE.ReplaceStencilOp,
      });

      const po = new THREE.Mesh(planeGeom, planeMat);
      po.onAfterRender = function (renderer) {
        renderer.clearStencil();
      };
      po.renderOrder = j + 1.1;
      objectss.add(stencilGroup);
      poGroup.add(po);
      planeObjects.push(po);
      scene.add(poGroup);      
  }

  const material = new THREE.MeshStandardMaterial({

    color: 0xffffff,
    metalness: 1,
    roughness: 1,
    clippingPlanes: Planearray,
    clipShadows: true,
    shadowSide: THREE.DoubleSide,
  });

  for (let i = 0; i < group.children.length; i++) {
    const clippedColorFront = new THREE.Mesh(
        group.children[i].geometry,
        material
      );
      clippedColorFront.renderOrder = 6;
      objectss.add(clippedColorFront);
  }
}


function animate() {
  requestAnimationFrame(animate);
  for ( let i = 0; i < planeobjectss.length; i ++ ) {

    const plane = Planearray[ i ];
    const po = planeobjectss[ i ];
    plane.coplanarPoint( po.position );
    po.lookAt(
        po.position.x - plane.normal.x,
        po.position.y - plane.normal.y,
        po.position.z - plane.normal.z,
    );

}
  camera2.position.copy(camera.position);
  camera2.position.sub(controls.target);
  camera2.position.setLength(300);
  camera2.lookAt(scene2.position);
  render();
}

createPlaneStencilGroup and is similar as implemented in the three.js stencil example.