[Solved] How to paint everything within a volume?

Hello I am creating a volume from 6 planes (X +, X-, Y +, Y-, Z +, Z-), each plane can have inclinations.


I need to paint whatever is within that volume generated from the intersections of the planes. So far I have done it with CSG, but I need to paint in real time to move the planes.


I have seen that ShaderMaterial can be used to do it but I have not succeeded, can you help me?

Thank you for your answer

You can modify the matrial of that mesh, passing 6 planes as uniforms and process them in shaders.
With the same principle, like it’s done with clipping planes: https://github.com/mrdoob/three.js/blob/cf0b35d65304d9c37f116a872a650473c78d1e6c/src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl.js
Just use the desired color of painting instead of discard.
But keep in mind, that renderer transfroms clipping planes, multiplying them with two matrices: https://github.com/mrdoob/three.js/blob/cf0b35d65304d9c37f116a872a650473c78d1e6c/src/renderers/webgl/WebGLClipping.js#L143

Hello @prisoner849 Thank you for your answer like this?

function paintInsideVolumeShader(planeXPos,
    planeZNeg) {
  const material = new THREE.ShaderMaterial({
      transparent: true,
      depthWrite: false,
      side: THREE.DoubleSide,
      uniforms: {
          XPos: {
              type: "3f",
              value: planeXPos.toArray(),
          XNeg: {
              type: "3f",
              value: planeXNeg.toArray(),
          YPos: {
              type: "3f",
              value: planeYPos.toArray(),
          YNeg: {
              type: "3f",
          ZPos: {
              type: "3f",
              value: planeZPos.toArray(),
          ZNeg: {
              type: "3f",
              value: planeZNeg.toArray(),
          color: {
          	type: "c",
            value: new THREE.Color(0xffff00),
      vertexShader: `
          void main() {
              gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      fragmentShader: `
          uniform vec3 color;
          uniform vec3 XPos;
          uniform vec3 XNeg;
          uniform vec3 YPos;
          uniform vec3 YNeg;
          uniform vec3 ZPos;
          uniform vec3 ZNeg;
          void main() {
              vec3 final_color;
              float distance = 0.0;
              float opacity = 1.0;
              // TODO: check inside volume, calc distance to plane? 
              if (distance <= 0.0 ) final_color = vec3(1.0, 0.0, 0.0); // inside volume color
              else final_color = color; // initial color 
              gl_FragColor = vec4(final_color, opacity);
  mesh.material = material;

In this case I would need to pass the center of each plane and the normal, but I don’t understand how to calculate the distance from which point to the plane to change colors

Another option is to use material stencils but it’s important to note that they’re very sensitive to the render order of the scene:

It does let you use any solid mesh volume you want to render the intersection with, though. Here’s the same fiddle but demonstrating intersections with a TorusKnotBufferGeometry:



Hello @gkjohnson Thank you for your answer.

I implemented your classes and changing the opacities looks good! although it paints up to THREE.GridHelper and the quad lines but it does what I need. I’m going to find a way not to paint the lines

Thanks to both @gkjohnson @prisoner849 !

though it paints up to THREE.GridHelper and the quad lines but it does what I need

Yes this is where controlling render order is important. You need ot make sure that everything you do want “painted” is rendered first (render order 0), then the stencil operations (which use render order 1 - 5), and then unpainted stuff is rendered after. There are other ways to more precisely control where the painting gets rendered but it does get more complicated.

In your fiddle if you set the gridHelper to render last it will not be pained with the stencil color. You’ll want to do something similar for the other components you don’t want painted:

gridHelper.renderOrder = 6;