How to render clipped objects as solid objects with predefined color?

Hi,

I am reffering to this method stackoverflow to easily let clipped objects appear capped by changing the fragmentShader before it compiles.

(I changed to this method rather than making stencils because I have multiple Objects with different capping-Colors, where I have got lost with all the stencilBuffers and renderOrders.)

It works well with:

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 );
        `
    )
};

However, I need to be able to modify the capping Color dynamically, because I will have multiple objects with different Colors, like so: (In my opinion, dead simple)

const color = new THREE.Color("#ff0000");
material.onBeforeCompile = function( shader ) {

    shader.fragmentShader = shader.fragmentShader.replace(

        '#include <output_fragment>',

        ` 
          vec3 backfaceColor = vec3( ` +  parseFloat(color.r).toFixed(2) +  `, ` +  parseFloat(color.g).toFixed(2) +   `, ` +  parseFloat(color.b).toFixed(2) +   `);       
          gl_FragColor = ( gl_FrontFacing ) ? vec4( outgoingLight, diffuseColor.a ) : vec4( backfaceColor , opacity );
        `
    )
};

The second approach was to set up some uniforms in the beginning.

Unfortunately neither method did not work - I guess my GLSL-Shaderskills are too poor!

Thanks for your help in advance.
Amlis

The material name defines the shader code used for rendering. If you have two meshes, using the same material, you can’t change the shader code to be different for each mesh, because there is only one shader code. So you can’t inject two different color values into the code.

You need to provide a uniform to the material and give it different values in the material constructor (which is what material.color is).

In your case, the easiest way is to use stencils.

Otherwise, you can either write a custom shader (and take care of illumination and other aspects yourself) or you can try to hack the material code in onBeforeCompile, for example, use emissive as the second color (backfaceColor). In that case, you need to add more replacements; to copy emissive to backfaceColor and make sure emissive is set to vec4(0.0) anywhere in the shader code (instead of the code using emissive uniform).

Thanks for your answer.
I kind of guessed I needed to change from MeshBasicMaterial to ShaderMaterial and define uniforms in advance. Its now working with the following code:

const _VS = `
#include <clipping_planes_pars_vertex>

void main() {

  #include <begin_vertex>

  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  #include <project_vertex>
  #include <clipping_planes_vertex>

}
`;

const _FS = `
uniform vec3 frontColor;
uniform vec3 backColor;

#include <clipping_planes_pars_fragment>
void main() {
  #include <clipping_planes_fragment>

  gl_FragColor = ( gl_FrontFacing ) ? vec4(frontColor,1.0 ) : vec4(backColor,1.0 );
}
`;

 material  = new THREE.ShaderMaterial({
      uniforms: {
        frontColor: {value: new THREE.Color(255, 0, 0)},
        backColor: {value: new THREE.Color(0, 0, 255)},
      },
      vertexShader: _VS,
      fragmentShader: _FS,
      clipping: true,
      side: THREE.DoubleSide,
    });

Using Stencils on multiple Objects, I always ran into the problem, that the last Clipping Plane also appeard on sibling Objects that actually have had their own clipping planes (with its own color-settings). see diagram below.

Perhaps someone has an Idea of my misconceptional technique :slight_smile:

Best regards

@anhelmphono

Maybe it will not work in your case, but here is another approach for clipping:

– Pavel

1 Like

@PavelBoytchev
I used similar idea, with painting of back faces with a solid color, for example, to make that yellow sandbed :slight_smile: Didn’t double the objects, just used gl_FrontFacing in patched materials.

1 Like

@prisoner849

Yeah, it’s an interesting approach to check gl_FrontFacing. I can’t recall whether there is an option in Three.js to provide a pair of materials – one for front facing triangles and a second one for back facing, without the need of custom shaders. SceneUtils.createMultiMaterialObject can be used for this, but it doubles the objects.

– Pavel

As far as I remember, clipping planes in THREE work in a way where fragment’s world coordinates are checked against being on one or another side of the plane, and fragments coming from the “wrong” side of the plane are simply discarded.

Since you’re writing a custom shader, you can relatively easily implement your own clipping planes or any other conditions (not necessarily a shape based) to discard fragments.

In your case, maybe there is a way to give objects IDs, pass them into a fragment shader, and apply different clipping logic based on the ID, might also help making stencil operations simpler.

In previous versions of THREE there was no use of gl_FrontFacing (at least for Lambertian material).

In one of the latest versions I checked, the code now uses gl_FrontFacing as a part of illumination calculation.

Figured that out when writing a JS function to make materials translucent (via .onBeforeCompile).

2 Likes