Threejs / EffectComposer - 交互式屏蔽 UnrealBloomPass

Threejs / EffectComposer - Interactively masking an UnrealBloomPass

因此,我尝试使用 EffectComposer 动态屏蔽 UnrealBloomPass,但我得到了意想不到的结果。我不确定我是否在这里遗漏了一个关键概念,或者我是否应该尝试以不同的方式实现这一点。任何输入将不胜感激。

作曲家被设置到这些层中:

我一直在尽可能密切地关注教程和示例,但仍然没有骰子。提前致谢。

 <!DOCTYPE html>
<html lang="en">
    <head>
        <title>BloomShader Mask</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body {
                color: #fff;
                font-family:Monospace;
                font-size:13px;
                text-align:center;
                background-color: #fff;
                margin: 0px;
                overflow: hidden;
            }
            #info {
                position: absolute;
                top: 0px;
                width: 100%;
                padding: 5px;
            }
            #info p {
                max-width: 600px;
                margin-left: auto;
                margin-right: auto;
                padding: 0 2em;
            }
            a {
                color: #2983ff;
            }
        </style>
    </head>
    <body>
        <div id="container"></div>
        <script src="./build/three.js"></script>
        <script src="js/libs/stats.min.js"></script>
        <script src="js/libs/dat.gui.min.js"></script>
        <script src="js/controls/OrbitControls.js"></script>
        <script src="js/loaders/GLTFLoader.js"></script>
        <script src="js/postprocessing/EffectComposer.js"></script>
        <script src="js/postprocessing/RenderPass.js"></script>
        <script src="js/postprocessing/ShaderPass.js"></script>
        <script src="js/postprocessing/MaskPass.js  "></script>
        <script src="js/shaders/CopyShader.js"></script>
        <script src="js/shaders/LuminosityHighPassShader.js"></script>
        <script src="js/postprocessing/UnrealBloomPass.js"></script>
    <script id="gradientShader" type="shader-code">
      uniform vec2 resolution;
      uniform vec2 mousePosition;
      void main() {
        vec2 pos = (gl_FragCoord.xy - mousePosition.xy * 1.2) / resolution.xy;
        gl_FragColor = vec4(1.0, pos.x, pos.y, 1.0);
      }
    </script>
    <script>
      var clock = new THREE.Clock();
      var stats;
      var gui;
      var sceneHexGradient;
      var sceneMask;
      var camera; 
      var orbitControls;
      var pointLight;
      var composer;
      var mixer;
      var maskerMesh;
      var bloomPass;
      var width = 0;
      var height = 0;
      var cameraZoom = 120;
      var mousePositionY = 0;

      var params = {
          exposure: 2,
          bloomStrength: .81,
          bloomThreshold: 0,
          bloomRadius: .05,
          cameraZoom: cameraZoom
      };

      function init() {
        // Init vars (DOM needed)
        gui = initGui();
        stats = initStats();
        width = window.innerWidth;
        height = window.innerHeight;
        pixelRatio = window.devicePixelRatio;

        // Renderer
        var webGLRenderer = new THREE.WebGLRenderer({ antialias: true });
        webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
        webGLRenderer.setPixelRatio(pixelRatio);
        webGLRenderer.setSize(width , height);
        webGLRenderer.toneMapping = THREE.ReinhardToneMapping;
        webGLRenderer.shadowMap.enabled = true;
        appendChild(webGLRenderer.domElement);

        // Scene
        sceneHexGradient = new THREE.Scene();
        sceneMasker = new THREE.Scene();

        // Camera
        camera = new THREE.OrthographicCamera(-width, width, height, -height, -1000, 1000);
        camera.zoom = cameraZoom;
        camera.lookAt(new THREE.Vector3(0, 0, 0));
        camera.updateProjectionMatrix();

        sceneHexGradient.add(camera);
        sceneMasker.add(camera);

        // Orbit
        orbitControls = new THREE.OrbitControls(camera, webGLRenderer.domElement);
        orbitControls.maxPolarAngle = Math.PI * 0.5;
        orbitControls.minDistance = 1;
        orbitControls.maxDistance = 10;

        // Draw onto Scenes
        drawGradientToScene(sceneHexGradient);
        drawHexToScene(sceneHexGradient);
        drawMaskerToScene(sceneMasker);

        // ShaderPass
        var hexgradientPass = new THREE.RenderPass(sceneHexGradient, camera);
        hexgradientPass.clear = false;

        var effectCopyPass = new THREE.ShaderPass(THREE.CopyShader);
        effectCopyPass.renderToScreen = true;

        var maskingPass = new THREE.MaskPass(sceneMasker, camera);
        maskingPass.clear = true;
        // var maskingPass = new THREE.RenderPass(sceneMasker, camera);
        // maskingPass.renderToScreen = true;

        bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
        bloomPass.renderToScreen = true;
        bloomPass.threshold = params.bloomThreshold;
        bloomPass.strength = params.bloomStrength;
        bloomPass.radius = params.bloomRadius;

        var clearMaskPass = new THREE.ClearMaskPass();

        composer = new THREE.EffectComposer(webGLRenderer);
        composer.renderTarget1.stencilBuffer = true;
        composer.renderTarget2.stencilBuffer = true;
        composer.setSize(width, height);
        composer.addPass(hexgradientPass); 
        composer.addPass(maskingPass);
        // composer.addPass(maskingVerticalBlurPass);
        composer.addPass(bloomPass);
        composer.addPass(clearMaskPass);
        composer.addPass(effectCopyPass);

        render();

        function initStats() { // Debug only 
          var stats = new Stats();
          stats.setMode(0);
          stats.domElement.style.position = 'absolute';
          stats.domElement.style.left = '0px';
          stats.domElement.style.top = '0px';
          appendChild(stats.dom);
          return stats;
        }

        function initGui() { // Debug only
                gui = new dat.GUI();

          gui.add(params, 'exposure', 0.1, 2 ).onChange((value) => {
            webGLRenderer.toneMappingExposure = Math.pow(value, 4.0);
          });

          gui.add(params, 'bloomThreshold', 0.0, 1.0).onChange((value) => {
            bloomPass.threshold = Number(value);
          });

          gui.add(params, 'bloomStrength', 0.0, 3.0).onChange((value) => {
            bloomPass.strength = Number(value);
          });

          gui.add(params, 'bloomRadius', 0.0, 1.0).step(0.01).onChange((value) => {
            bloomPass.radius = Number(value);
          });

          gui.add(params, 'cameraZoom', 100, 150).onChange((value) => {
            camera.zoom = Number(value);
            camera.updateProjectionMatrix();
          });

          return gui;
        }

        function appendChild(domElement) {
          var container = document.getElementById('container');
          if (container) {
            return container.appendChild(domElement);
          }
          return false;
        }

        function drawGradientToScene(scene) {
          var gradientUniforms = {};
          gradientUniforms["resolution"] = { type:'v2', value:new THREE.Vector2(width, height)};
          gradientUniforms['mousePosition'] = { type:'v2', value:new THREE.Vector2(0, 0) };
          var shaderCode = document.getElementById('gradientShader').innerHTML;
          var material = new THREE.ShaderMaterial({ uniforms:gradientUniforms, fragmentShader:shaderCode });
          var geometry = new THREE.PlaneBufferGeometry(width, height);
          var mesh = new THREE.Mesh(geometry, material);
          scene.add(mesh);
        }

        function drawHexToScene(scene) {
          var hexes = [];
          var colCount = 100;
          var rowCount = 100;
          var hexDiameter = 1;
          var xStart = -(colCount) * hexDiameter * 0.5;
          var rowSpace = Math.sqrt(3) * (hexDiameter * 0.5);
          var yStart = (rowCount - 1) * (rowSpace * 0.5);

          var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
          hexGeom.rotateX(Math.PI * 0.5);
          for (let j = 0; j < rowCount; j++) {
            for (let i = 0; i < colCount + (j % 2 === 0 ? 0 : 1); i++) {
              let hex = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
                color: 0x000000,
                wireframe: false,
              }));
              var x = (xStart + i * hexDiameter + (j % 2 === 0 ? 0.5 * hexDiameter : 0));
              var y = (yStart - j * rowSpace);
              hex.position.set(x, y, 0);
              hexes.push(hex);
              scene.add(hex);
            }
          }
        }

        function drawMaskerToScene(scene) {
          var boxGeometry = new THREE.BoxGeometry(width, height, 10);
          var basicMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
          maskerMesh = new THREE.Mesh(boxGeometry, basicMaterial);
          maskerMesh.position.y = -height;
          scene.add(maskerMesh);
        }  

        function onResze() {
          camera.position.set(0, 0, -15);
          camera.zoom = cameraZoom;
          camera.updateProjectionMatrix();
                  composer.setSize(width, height);
        }

        function onMouseMove(event) {
          mousePositionY = event.clientY; 
        }

        function render() {
          webGLRenderer.autoClear = false;
          stats.update();

          var delta = clock.getDelta();
          // orbitControls.update(delta);

          requestAnimationFrame(render);

          maskerMesh.position.y = mousePositionY;
          // composer.setSize(window.innerWidth, window.innerHeight);
          composer.render();
        }

        window.addEventListener('mousemove', onMouseMove);
    } // End - init 
    // Listeners
    window.onload = init;
    window.onresize = init.onResze;
        </script>
    </body>
</html>

这实际上是一个问题的组合。主要问题是在通行证上设置标志,以清除或渲染到屏幕。这不是一件容易调试的事情:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>BloomShader Mask</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, 
    minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            color: #fff;
            font-family:Monospace;
            font-size:13px;
            text-align:center;
            background-color: #fff;
            margin: 0px;
            overflow: hidden;
        }
        #info {
            position: absolute;
            top: 0px;
            width: 100%;
            padding: 5px;
        }
        #info p {
            max-width: 600px;
            margin-left: auto;
            margin-right: auto;
            padding: 0 2em;
        }
        a {
            color: #2983ff;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <script src="./build/three.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="js/libs/dat.gui.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
    <script src="js/loaders/GLTFLoader.js"></script>
    <script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/ClearPass.js"></script>
    <script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/MaskPass.js  "></script>
    <script src="js/shaders/CopyShader.js"></script>
    <script src="js/shaders/LuminosityHighPassShader.js"></script>
<script src="js/postprocessing/UnrealBloomPass.js"></script>
<script id="gradientShader" type="shader-code">
  uniform vec2 resolution;
  uniform vec2 mousePosition;
  void main() {
    vec2 pos = ((gl_FragCoord.xy - mousePosition.xy * 1.3) / resolution.xy);
    gl_FragColor = vec4(1.0, pos.x, pos.y, 1.0);
  }
</script>
    <script>
  var clock = new THREE.Clock();
  var stats;
  var gui;
  var sceneHexGradient;
  var sceneBloomMasker;
  var sceneMasker;
  var camera; 
  var orbitControls;
  var pointLight;
  var composer;
  var mixer;
  var maskerMesh;
  var bloomPass;
  var width = 0;
  var height = 0;
  var cameraZoom = 120;
  var mousePositionY = 0;

        var params = {
            exposure: 2, //1.1
            bloomStrength: 1.75,
            bloomThreshold: 0,
    bloomRadius: .16,
    cameraZoom: cameraZoom
        };

  function init() {
    // Init vars (DOM needed)
    gui = initGui();
    stats = initStats();
    width = window.innerWidth;
    height = window.innerHeight;
    pixelRatio = window.devicePixelRatio;

    // Camera
    camera = new THREE.OrthographicCamera(-width, width, height, -height, -1000, 1000);
    camera.zoom = cameraZoom;
    camera.lookAt(new THREE.Vector3(0, 0, 0));
    camera.updateProjectionMatrix();

    // Renderer
    var webGLRenderer = new THREE.WebGLRenderer({ antialias: true });
    webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
    webGLRenderer.setPixelRatio(pixelRatio);
    webGLRenderer.setSize(width , height);
    webGLRenderer.toneMapping = THREE.ReinhardToneMapping;
    webGLRenderer.shadowMap.enabled = true;
    webGLRenderer.autoClear = false;
    appendChild(webGLRenderer.domElement);

    // Scene
    sceneHexGradient = new THREE.Scene();
    sceneBloom = new THREE.Scene();
    sceneMasker = new THREE.Scene();

    sceneHexGradient.add(camera);
    sceneBloom.add(camera);
    sceneMasker.add(camera);

    // Lights (are they needed ? ) 
    sceneBloom.add(new THREE.AmbientLight(0xffffff));

    // Orbit
    orbitControls = new THREE.OrbitControls(camera, webGLRenderer.domElement);
    orbitControls.maxPolarAngle = Math.PI * 0.5;
    orbitControls.minDistance = 1;
    orbitControls.maxDistance = 10;

    // Draw onto Scenes
    drawGradientToScene(sceneHexGradient);
    drawHexToScene(sceneHexGradient);
    drawMaskerToScene(sceneMasker);

    bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
    bloomPass.threshold = params.bloomThreshold;
    bloomPass.strength = params.bloomStrength;
    bloomPass.radius = params.bloomRadius;

    // ShaderPasses
    var clearPass       = new THREE.ClearPass();
    var clearMaskPass   = new THREE.ClearMaskPass();
    var hexgradientPass = new THREE.RenderPass(sceneHexGradient, camera);
    var maskingPass     = new THREE.MaskPass(sceneMasker, camera);
    var outputPass      = new THREE.ShaderPass(THREE.CopyShader);

            outputPass.renderToScreen = true;

    var parameters = {
                minFilter: THREE.LinearFilter,
                magFilter: THREE.LinearFilter,
                format: THREE.RGBFormat,
                stencilBuffer: true
            };

            var renderTarget = new THREE.WebGLRenderTarget(width, height, parameters);

    composer = new THREE.EffectComposer(webGLRenderer, renderTarget);

    composer.addPass(clearPass);
    composer.addPass(hexgradientPass); 
    composer.addPass(maskingPass);
    composer.addPass(bloomPass);
    composer.addPass(clearMaskPass);
    composer.addPass(outputPass);

    function initStats() { // Debug only 
      var stats = new Stats();
      stats.setMode(0);
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.left = '0px';
      stats.domElement.style.top = '0px';
      appendChild(stats.dom);
      return stats;
    }

    function initGui() { // Debug only
            gui = new dat.GUI();

      gui.add(params, 'exposure', 0.1, 2 ).onChange((value) => {
        webGLRenderer.toneMappingExposure = Math.pow(value, 4.0);
      });

      gui.add(params, 'bloomThreshold', 0.0, 1.0).onChange((value) => {
        bloomPass.threshold = Number(value);
      });

      gui.add(params, 'bloomStrength', 0.0, 3.0).onChange((value) => {
        bloomPass.strength = Number(value);
      });

      gui.add(params, 'bloomRadius', 0.0, 1.0).step(0.01).onChange((value) => {
        bloomPass.radius = Number(value);
      });

      gui.add(params, 'cameraZoom', 100, 150).onChange((value) => {
        camera.zoom = Number(value);
        camera.updateProjectionMatrix();
      });

      return gui;
    }

    function appendChild(domElement) {
      var container = document.getElementById('container');
      if (container) {
        return container.appendChild(domElement);
      }
      return false;
    }

    function getRandomNumber(min = 1, max = 100) {
      return Math.floor(Math.random() * (+max - +min)) + +min; 
    }

    function getArrayOfRandomNumbers(count = 1, min = 1, max = 100) {
      const outArr = [];
      for( i = 0; i < count; ++i) {
        outArr.push(getRandomNumber(min, max));
      }
      return outArr.sort();
    }

    function drawBlackoutMask(scene) {
      // var x = 0, y = 0;
      // var hexDiameter = 2;
      // var shapeWidth = 10;

      // var facets = getRandomNumber(1, shapeWidth);
      // var xValuesArr = getArrayOfRandomNumbers(facets, 0, shapeWidth);
      // var yValuesArr = getArrayOfRandomNumbers(facets, 0, shapeWidth);

      // var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
      // hexGeom.rotateX(Math.PI * 0.5);
      // var boxGeometry = new THREE.BoxGeometry(10, 10, 10);
      // var mesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
      // mesh.position.x = 0;
      // mesh.position.y = 0;
      // scene.add(mesh);  

      // for(var i = 0; i < facets; i++) {
      //   var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
      //   var mesh = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
      //     color: 0xff0000,
      //     wireframe: false,
      //   }));
      //   mesh.position.x = xValuesArr[i];
      //   mesh.position.y = yValuesArr[i];
      //   scene.add(mesh);
      // }          
    }

    function drawMaskerToScene(scene) {
      var boxGeometry = new THREE.CylinderBufferGeometry(width, height, 10);
      // var basicMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
      boxGeometry.computeBoundingBox();
      var material = new THREE.ShaderMaterial({
        uniforms: {
          color1: {
            value: new THREE.Color(0xffffff)
          },
          color2: {
            value: new THREE.Color(0x000000)
          },
          bboxMin: {
            value: boxGeometry.boundingBox.min
          },
          bboxMax: {
            value: boxGeometry.boundingBox.max
          }
        },
        vertexShader: `
          uniform vec3 bboxMin;
          uniform vec3 bboxMax;

          varying vec2 vUv;

          void main() {
            vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
          }
        `,
        fragmentShader: `
          uniform vec3 color1;
          uniform vec3 color2;

          varying vec2 vUv;

          void main() {
            gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
          }
        `,
        wireframe: false
      });
      maskerMesh = new THREE.Mesh(boxGeometry, material); //basicMaterial);
      // maskerMesh.scale.set(1,1,1);
      // maskerMesh.position.y = -height;
      scene.add(maskerMesh);
    }  

    function drawGradientToScene(scene) {
      var gradientUniforms = {};
      gradientUniforms["resolution"] = { type:'v2', value:new THREE.Vector2(width, height)};
      gradientUniforms['mousePosition'] = { type:'v2', value:new THREE.Vector2(0, 0) };
      var shaderCode = document.getElementById('gradientShader').innerHTML;
      var material = new THREE.ShaderMaterial({ uniforms:gradientUniforms, fragmentShader:shaderCode });
      var geometry = new THREE.PlaneBufferGeometry(width, height);
      var mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
    }

    function drawHexToScene(scene) {
      var hexes = [];
      var colCount = 100;
      var rowCount = 100;
      var hexDiameter = 1;
      var xStart = -(colCount) * hexDiameter * 0.5;
      var rowSpace = Math.sqrt(3) * (hexDiameter * 0.5);
      var yStart = (rowCount - 1) * (rowSpace * 0.5);

      var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
      hexGeom.rotateX(Math.PI * 0.5);
      for (let j = 0; j < rowCount; j++) {
        for (let i = 0; i < colCount + (j % 2 === 0 ? 0 : 1); i++) {
          let hex = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
            color: 0x000000,
            wireframe: false,
          }));
          var x = (xStart + i * hexDiameter + (j % 2 === 0 ? 0.5 * hexDiameter : 0));
          var y = (yStart - j * rowSpace);
          hex.position.set(x, y, 0);
          hexes.push(hex);
          scene.add(hex);
        }
      }
    }

    function onResze() {
      camera.position.set(0, 0, -15);
      camera.zoom = cameraZoom;
      camera.updateProjectionMatrix();
              composer.setSize(width, height);
    }

    function onMouseMove(event) {
      mousePositionY = event.clientY; 
    }

    function render() {
      webGLRenderer.autoClear = false;
      stats.update();

      var delta = clock.getDelta();

      // orbitControls.update(delta);

      requestAnimationFrame(render);

      maskerMesh.position.y = mousePositionY;
      webGLRenderer.clear();
      composer.render(delta);
    }

    window.addEventListener('mousemove', onMouseMove);
    render();
} // End - init 
// Listeners
window.onload = init;
window.onresize = init.onResze;
    </script>
</body>