ThreeJS Selective Bloom 禁用场景背景的光晕

ThreeJS Selective Bloom Disable Bloom For Scene Background

我正在使用 ThreeJS 生成选择性光晕效果,以便使用发光贴图作为滤镜在对象上显示发光部分。最近我尝试启用我的环境贴图,发现光晕会影响 scene.background 外观。

这是我生成环境贴图的方式:

function createEnvironment( hdr ) {
    new RGBELoader().setDataType( THREE.UnsignedByteType ).load( hdr, function ( texture ) {        
    
        const pmremGenerator = new THREE.PMREMGenerator( renderer )
        pmremGenerator.compileEquirectangularShader()
        
        const envMap = pmremGenerator.fromEquirectangular( texture ).texture
        scene.background = texture;
        scene.environment = envMap
        
        texture.dispose()
        pmremGenerator.dispose()
    } );
}

此刻环境地图疯狂发光。我稍微研究了一下,但找不到解决方案,因为 scene.background 不使用 material,而且我找不到其他方法。

我现在卡住了。我怎样才能让选择性绽放不影响scene.background

这是我使用的选择性布隆代码

body{
  overflow: hidden;
  margin: 0;
}
<script type="x-shader/x-vertex" id="vertexshader">
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
  uniform sampler2D baseTexture;
  uniform sampler2D bloomTexture;
  varying vec2 vUv;
  void main() {
    gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
  }
</script>
<script type="module">
console.clear();
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.127.0/build/three.module.js';
import {GLTFLoader} from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/loaders/GLTFLoader.js';

import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/controls/OrbitControls.js';

import { EffectComposer } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'https://cdn.jsdelivr.net/npm/three@0.127.0/examples/jsm/postprocessing/UnrealBloomPass.js';

let model;

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(7.7, 2.5, 7.2);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", e => {console.log(camera.position)})

let light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

let uniforms = {
  globalBloom: {value: 1}
}

let loader = new GLTFLoader();
loader.load( "https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/turntable_121.glb", function ( gltf ) {
    model = gltf.scene;
  let emssvTex = new THREE.TextureLoader().load("https://cdn.jsdelivr.net/gh/MJegerlehnerBio/bloom-solution/RecordPlayer_Emission.jpeg", function( texture ) {
    texture.flipY = false
    texture.encoding = THREE.sRGBEncoding
  })    
  model.traverse( function ( child ) {
    if ( child.isMesh ) {
        child.material.emissiveMap = emssvTex;
      child.material.onBeforeCompile = shader => {
        shader.uniforms.globalBloom = uniforms.globalBloom;
        shader.fragmentShader = `
            uniform float globalBloom;
          ${shader.fragmentShader}
        `.replace(
            `#include <dithering_fragment>`,
          `#include <dithering_fragment>
            if (globalBloom > 0.5){
                gl_FragColor = texture2D( emissiveMap, vUv );
            }
          `
        );
        console.log(shader.fragmentShader);
      }
    }
  });
  model.scale.setScalar(40);
  scene.add(model);
});

// bloom
const renderScene = new RenderPass( scene, camera );

const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 2, 0, 0 );

const bloomComposer = new EffectComposer( renderer );
bloomComposer.renderToScreen = false;
bloomComposer.addPass( renderScene );
bloomComposer.addPass( bloomPass );

const finalPass = new ShaderPass(
  new THREE.ShaderMaterial( {
    uniforms: {
      baseTexture: { value: null },
      bloomTexture: { value: bloomComposer.renderTarget2.texture }
    },
    vertexShader: document.getElementById( 'vertexshader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
    defines: {}
  } ), "baseTexture"
);
finalPass.needsSwap = true;

const finalComposer = new EffectComposer( renderer );
finalComposer.addPass( renderScene );
finalComposer.addPass( finalPass );

window.onresize = function () {

  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( innerWidth, innerHeight );

  bloomComposer.setSize( innerWidth, innerHeight );
  finalComposer.setSize( innerWidth, innerHeight );

};

renderer.setAnimationLoop( _ => {
    
  renderer.setClearColor(0x000000);
  uniforms.globalBloom.value = 1;
  
  bloomComposer.render();
  
  renderer.setClearColor(0x404040);
  uniforms.globalBloom.value = 0;
  
    finalComposer.render();
  //renderer.render(scene, camera);
})

</script>

在渲染循环中,使用 scene.background 而不是 renderer.setClearColor

定义场景背景

let scback = {
   bloomOn: new THREE.Color(0x000000),
   bloomOff: new THREE.Color(0x404040) // or define something else, a cubetexture, for example.
}

然后在渲染循环中:

renderer.setAnimationLoop( _ => {
    
  scene.background = scback.bloomOn; // must be pure black
  uniforms.globalBloom.value = 1;
  
  bloomComposer.render();
  
  scene.background = scback.bloomOff; // anything else you want to show
  uniforms.globalBloom.value = 0;
  
  finalComposer.render();
})

PS 刚刚从头开始,还没有测试。 :)