使用蒙版隐藏对象的一部分 (EffectComposer)

Hide a part of an object using masks (EffectComposer)

问题描述

我目前正在发现 three.jsEffectComposer,我正在寻找使用遮罩隐藏对象的一部分的方法。

假设您有一个简单的场景:相机和圆柱体之间的立方体。立方体将起到面具的作用,并将圆柱体隐藏在它后面:

已测试解决方案

我玩过以下 ThreeJS 示例并尝试调整它们以获得我想要的结果,但没有成功:

我猜问题出在通行证的使用上。

我尝试了两种解决方案(查看下面的代码片段以了解添加的通行证类型):

1- 添加 maskPass,然后添加 renderPass,以便我的场景渲染仅在我的掩码内绘制

  composer = new THREE.EffectComposer(renderer, renderTarget);
  composer.addPass(maskPass1);
  composer.addPass(renderPass);
  composer.addPass(clearMaskPass);
  composer.addPass(outputPass);

2- 添加 renderPass,然后是倒置遮罩,以及一个清除像素的 clearPass

  maskPass1.inverse = true;
  composer = new THREE.EffectComposer(renderer, renderTarget);
  composer.addPass(renderPass);
  composer.addPass(maskPass1);
  composer.addPass(clearPass);
  composer.addPass(clearMaskPass);
  composer.addPass(outputPass);

在下面,您将找到一个代码片段,其中显示了我到目前为止所做的工作。我用了一个 DotScreenPass 只是为了看看 maskPass 的效果。

在下图中,您会在左侧看到我使用代码段得到的结果,在右侧看到我想要的结果

代码片段

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>

经过进一步研究,我找到了两个解决方案,但它们都没有使用 EffectComposer(这对我来说不是问题)。

第一个:深度缓冲区(Z 缓冲区)

思路如下:

  1. 创建两个场景,第一个包含扮演面具角色的对象,另一个包含您的正常场景
  2. 防止渲染器写入颜色缓冲区,仅在深度缓冲区(z 缓冲区)和 "render" 您的遮罩场景中。 z 缓冲区现在包含蒙版场景的深度信息
  3. 启用回写到颜色缓冲区,并渲染您的正常场景。由于深度比较,您的正常场景的某些片段将不会被渲染

谢谢@WestLangley 这个问题:

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

}

第二种解决方案:模板缓冲区

我没有测试这个解决方案,但 jsfiddle 看起来不错!

看到这个问题:

以及关联的 jsfiddle:http://jsfiddle.net/g29k91qL/21/