I am currently discovering the EffectComposer
of three.js and I am looking for the method to hide a part of an object by another using masks.
Supposing you have a simple scene : a cube between the camera and a cylinder. The cube will have the role of the mask and hide the cylinder behind it :
I have played with the following examples of ThreeJS and tried to tweak them to get the result I want, but without success :
The problem comes from the use of the passes I guess.
I tried two solutions (check the snippet below to know the type of the passes added) :
1- Add the maskPass and then the renderPass so that the render of my scene will be drawn only inside my mask
composer = new THREE.EffectComposer(renderer, renderTarget);
composer.addPass(maskPass1);
composer.addPass(renderPass);
composer.addPass(clearMaskPass);
composer.addPass(outputPass);
2- Add the renderPass, then the inverted mask, and a clearPass to remove the pixels
maskPass1.inverse = true;
composer = new THREE.EffectComposer(renderer, renderTarget);
composer.addPass(renderPass);
composer.addPass(maskPass1);
composer.addPass(clearPass);
composer.addPass(clearMaskPass);
composer.addPass(outputPass);
Below, you will find a code snippet showing what I've done until now. I used a DotScreenPass
just to see the effect of the maskPass.
On the image below, you'll see the result I get using the snippet on the left side and the result I want on the right side
var composer, renderer;
var box, torus;
init();
animate();
function init() {
// Setup renderer
renderer = new THREE.WebGLRenderer({antialias: false});
renderer.setClearColor(0xe0e0e0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
// Setup scenes
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 10;
var scene1 = new THREE.Scene();
var scene2 = new THREE.Scene();
// Add objects
box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4));
box.rotateY(Math.PI / 6);
box.rotateX(-Math.PI / 6);
scene1.add(box);
torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 16, 32), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene2.add(torus);
// Create passes for composer
var clearPass = new THREE.ClearPass();
var clearMaskPass = new THREE.ClearMaskPass();
var maskPass1 = new THREE.MaskPass(scene1, camera);
var maskPass2 = new THREE.MaskPass(scene2, camera);
maskPass1.inverse = true
var renderPass = new THREE.RenderPass(scene2, camera);
var screenDotPass = new THREE.DotScreenPass();
var outputPass = new THREE.ShaderPass(THREE.CopyShader);
outputPass.renderToScreen = true;
var renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: true
});
// Create composer and add passes
composer = new THREE.EffectComposer(renderer, renderTarget);
composer.addPass(renderPass);
composer.addPass(maskPass1);
composer.addPass(screenDotPass);
composer.addPass(clearMaskPass);
composer.addPass(outputPass);
}
function animate() {
requestAnimationFrame(animate);
var time = performance.now() * 0.001;
renderer.clear();
composer.render(time);
}
body
{
background-color: #000;
margin: 0px;
overflow: hidden;
}
<div id="container"></div>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/CopyShader.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/DotScreenShader.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/EffectComposer.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/ClearPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/TexturePass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/ShaderPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/MaskPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/DotScreenPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/RenderPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/DotScreenPass.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/Detector.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/libs/stats.min.js"></script>
After further researches, I found two solutions, but they do not use EffectComposer (which is not a problem for me).
The idea is the following :
Thanks you @WestLangley this question : How to write to zbuffer only with three.js
var composer, renderer;
var box, sphere;
var scene1, scene2;
var camera;
init();
animate();
function init() {
// Setup renderer
renderer = new THREE.WebGLRenderer({
antialias: false
});
renderer.setClearColor(0xe5e5e5);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// VERY IMPORTANT
renderer.autoClear = false ;
document.body.appendChild(renderer.domElement);
// Setup scenes
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 15;
scene1 = new THREE.Scene();
scene2 = new THREE.Scene();
// Add objects
box = new THREE.Mesh(new THREE.BoxGeometry(4, 4, 4), new THREE.MeshBasicMaterial({
color: 0x008800
}));
box.rotateY(Math.PI / 6);
box.rotateX(-Math.PI / 6);
box.position.z += 1;
scene1.add(box);
sphere = new THREE.Mesh(new THREE.SphereGeometry(4, 16, 16), new THREE.MeshBasicMaterial({
color: 0xff0000
}));
scene2.add(sphere);
}
function animate() {
requestAnimationFrame(animate);
// Manually clear the renderer
renderer.clear();
// Sets which color components to enable or to disable when drawing or rendering to a WebGLFramebuffer
renderer.context.colorMask(false, false, false, false); // R, G, B, A
renderer.render(scene1, camera);
// Enable back the writing into the color and alpha component
renderer.context.colorMask(true, true, true, true);
renderer.render(scene2, camera);
}
I didn't tested this solution, but the jsfiddle seems nice !
See this question : Three.js usage with stencil buffer
And the associated jsfiddle : http://jsfiddle.net/g29k91qL/21/