7

In Three.js, I have a 3d object where I am using local clipping planes to only render a part of the object.

However, since 3d objects are "hollow" (meaning only the outer surface is rendered), when we clip anything off that surface we can "see into" the object. Here's an example of what I mean, clipping a corner off a cube. Notice how we can see the backside of the opposite corner.

I would like to give the appearance of the object being solid. Based on this issue, it seems that the best way to accomplish this is to create a surface over the clipped region, thus capping the hole and making the object appear like it isn't hollow.

My question is, how do I know where to build this surface? Does Three.js provide a way to get a list of vertices that intersect between a plane and any arbitrary surface? If not, how might I approach this problem myself?

I found this question, but the author didn't describe how they solved the problem I am having here.

Wilt
  • 41,477
  • 12
  • 152
  • 203
Cameron Edwards
  • 198
  • 1
  • 6
  • Can you provide a live link to a realistic use case that demonstrates your true constraints? Are textures involved? What material are you using? What are you willing to compromise? – WestLangley May 07 '16 at 18:43
  • @WestLangley Sure, this [simple example](http://threejs.org/examples/#webgl_clipping) demonstrates my problem. I would like to cover up the openings so you can't see inside the shape. I am not using textures, just a basic MeshPhongMaterial. Performance is my priority, so if this can only be done in a performant way on simple shapes, then so be it. But I would like to know if it's possible to do this in a general way that can be applied to any shape. – Cameron Edwards May 07 '16 at 19:35

2 Answers2

12

You want to render a clipped surface as if it were a solid -- i.e., not hollow.

You can achieve that effect with MeshPhongMaterial -- or any three.js material for that matter -- with a simple hack to the material shader.

material.onBeforeCompile = function( shader ) {

    shader.fragmentShader = shader.fragmentShader.replace(

        '#include <output_fragment>',

        `
        vec3 backfaceColor = vec3( 0.4, 0.4, 0.4 );
        gl_FragColor = ( gl_FrontFacing ) ? vec4( outgoingLight, diffuseColor.a ) : vec4( backfaceColor, opacity );
        `
    )
};

This should look pretty good. It will require material.side = THREE.DoubleSide;

clipped torus

Alternatively, see https://threejs.org/examples/webgl_clipping_stencil.html.

three.js r.148

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • 1
    @WestLangley When using a texture I get z-fighting on the clipped surfaces. Is this something I can avoid? – Jeremy Knees Sep 27 '16 at 09:24
  • @WestLangley, I also get z fighting. I would like to know if it is possible to avoid this! – Wouter Coebergh Jul 27 '17 at 17:53
  • @WouterCoebergh It is best to make a new post and provide sufficient information to replicate the issue if you are unable to resolve it yourself. – WestLangley Jul 30 '17 at 04:18
  • 1
    I like this hack @WestLangley however if you happen to have another geometry within the hollow geometry, that geometry will still be rendered. as the depth test is on. am I right? – andrevenancio Dec 01 '17 at 17:31
  • 1
    @WestLangley. I tried doing the 'onBeforeCompile' code that you have provided but it does not solve the issue. Any idea what I might be doing wrong ? My geometry is a THREE.BufferGeometry() , material is MeshPhongMaterial() with side property = THREE.DoubleSide as you mentioned. Also, I have created the geometry by adding vertex positions and colors as the buffer attributes. – Rahul Pillai Aug 19 '20 at 21:24
  • This solution seems extremely simple compared to stencils (which have been ruining my last few weeks), but I haven't found a complete example implementing this fragmentShader. If it is supposedly as simple as adding the above code snippet to a MeshPhongMaterial, then it doesn't work on r125 or r127. – pedro pablo león jaramillo Jan 03 '23 at 22:26
  • @pedropabloleónjaramillo Updated to r.148 – WestLangley Jan 03 '23 at 23:17
  • ETA: In my case, it worked after setting material.transparent = false; This solution isn't compatible with models when you want opacity < 1. Also for those pursuing this, I had trouble with my STL-based geometries, where the gl_FrontFacing boolean didn't always recognize the correct inside/outside of the triangles. – pedro pablo león jaramillo Jan 03 '23 at 23:44
  • Thanks @WestLangley for your responsiveness! – pedro pablo león jaramillo Jan 03 '23 at 23:45
  • @WestLangley, when I used the fragment shader, it only replace the back face color, not filling the empty space. any idea? – Shin-Young Jung May 30 '23 at 06:43
  • @Shin-YoungJung Your model should be watertight. See https://discourse.threejs.org/t/stencil-clipping-gltf-weird-backfaces/41936. – WestLangley May 30 '23 at 20:13
3

I made a THREE.SectionHelper class which could be interesting if you want to set a different material/color for the inside of the mesh that you are clipping. Check a demo in this fiddle.

var sectionHelper = new THREE.SectionHelper( mesh, 0xffffff );
scene.add(sectionHelper);
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • this is not helpful if you have other items added to the scene. For example in your demo example if I change the wireframe helper color to red. wireframeHelper = new THREE.WireframeHelper(mesh, 0xff0000); all the edges behind would be visible. – Pranav Asthana Mar 31 '20 at 13:33