Three.js Bloom生效异常问题:请求修改示例并讲解场景遍历与材质释放
Got it, let's fix that bloom effect issue in your Three.js code, and also break down how to properly use scene.traverse and obj.material.dispose along the way.
First, let's diagnose why your current setup isn't producing the expected bloom (like bloomYes.png instead of bloomNot.png):
- The
RenderPassin your composer wasn't restricted to only render layer 1 (the bloom-specific objects), so it was applying bloom to the entire scene instead of just the glowing elements. - Your layering render order was reversed, causing the bloom effect to be covered up by the main scene in some cases.
- Repeated variable names (like using
objfor every mesh) made the code hard to debug and maintain.
Modified Working Code
<!DOCTYPE html> <html> <head> <style> body { overflow: hidden; margin: 0; } </style> </head> <body> <script src="https://threejs.org/build/three.min.js"></script> <script src='https://threejs.org/examples/js/postprocessing/EffectComposer.js'></script> <script src='https://threejs.org/examples/js/postprocessing/RenderPass.js'></script> <script src='https://threejs.org/examples/js/postprocessing/ShaderPass.js'></script> <script src='https://threejs.org/examples/js/shaders/CopyShader.js'></script> <script src="https://threejs.org/examples/js/shaders/FXAAShader.js"></script> <script src="https://threejs.org/examples/js/shaders/LuminosityHighPassShader.js"></script> <script src="https://threejs.org/examples/js/postprocessing/UnrealBloomPass.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> <script> var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, window.innerWidth/ window.innerHeight, 1, 1000); camera.position.set(20, 0, -20); camera.layers.enable(1); var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.autoClear = false; renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0x000000); document.body.appendChild(renderer.domElement); var controls = new THREE.OrbitControls(camera, renderer.domElement); var light = new THREE.DirectionalLight(0xffffff, 0.75); light.position.setScalar(100); scene.add(light); scene.add(new THREE.AmbientLight(0xffffff, 0.25)); // Non-bloom objects (layer 0) var obj1 = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 4), new THREE.MeshLambertMaterial({color: 0xf0f0f0})); obj1.layers.set(0); obj1.position.z = 0; scene.add(obj1); var obj2 = new THREE.Mesh(new THREE.BoxGeometry(12, 12, 1), new THREE.MeshLambertMaterial({color: 0x0000ff})); obj2.position.z = 2.5; obj2.layers.set(0); scene.add(obj2); var obj3 = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 4), new THREE.MeshLambertMaterial({color: 0xf0f0f0})); obj3.layers.set(0); obj3.position.z = 5; scene.add(obj3); var objRed = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 1), new THREE.MeshBasicMaterial({color: 0xff0000})); objRed.position.z = -2.5; objRed.layers.set(0); scene.add(objRed); var objGreen = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 1), new THREE.MeshBasicMaterial({color: 0x00ff00})); objGreen.position.z = 7.5; objGreen.layers.set(0); scene.add(objGreen); // Bloom objects (layer 1) var objBloomRed = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 1), new THREE.MeshBasicMaterial({color: 0xff0000})); objBloomRed.position.z = -2.25; objBloomRed.layers.set(1); scene.add(objBloomRed); var objBloomGreen = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 1), new THREE.MeshBasicMaterial({color: 0x00ff00})); objBloomGreen.position.z = 7.5; objBloomGreen.layers.set(1); scene.add(objBloomGreen); /** COMPOSER CONFIGURATION - FIXED */ // Create a render pass restricted to bloom layer (layer 1) const bloomRenderPass = new THREE.RenderPass(scene, camera); bloomRenderPass.camera.layers.set(1); // Only render bloom objects here const effectFXAA = new THREE.ShaderPass(THREE.FXAAShader); effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight); const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85 ); bloomPass.threshold = 0.21; bloomPass.strength = 1.2; bloomPass.radius = 0.55; bloomPass.renderToScreen = false; // Don't render bloom directly to screen yet // Final pass to copy bloom effect over the main scene const copyPass = new THREE.ShaderPass(THREE.CopyShader); copyPass.renderToScreen = true; const composer = new THREE.EffectComposer(renderer); composer.setSize(window.innerWidth, window.innerHeight); composer.addPass(bloomRenderPass); composer.addPass(effectFXAA); composer.addPass(bloomPass); composer.addPass(copyPass); var delta = 0; function render(){ requestAnimationFrame(render); delta += 0.05; // Sync color updates for bloom and matching non-bloom object const greenVal = Math.sin(delta); const blueVal = Math.cos(delta); objBloomRed.material.color.g = greenVal; objBloomRed.material.color.b = blueVal; objRed.material.color.g = greenVal; objRed.material.color.b = blueVal; renderer.clear(); // First render main scene (layer 0) camera.layers.set(0); renderer.render(scene, camera); // Then overlay the bloom effect renderer.clearDepth(); composer.render(); } render(); // Example: Proper resource cleanup with scene.traverse and material.dispose function cleanupScene() { // Traverse every object in the scene to clean up resources scene.traverse((obj) => { if (obj.isMesh) { // Dispose material to free GPU memory obj.material.dispose(); // Dispose geometry if it's not reused elsewhere obj.geometry.dispose(); } }); // Clean up composer resources too composer.dispose(); } // Optional: Attach cleanup to window unload to prevent memory leaks window.addEventListener('unload', cleanupScene); </script> </body> </html>
Key Fixes & Explanations
Restricted Bloom Rendering
- We added a dedicated
RenderPassthat only renders layer 1 (bloom objects) by settingbloomRenderPass.camera.layers.set(1). This ensures bloom is only applied to the intended glowing elements, not the entire scene. - Reversed the render order: first draw the main scene (layer 0), then overlay the bloom effect—this fixes the layering issue where bloom was being covered up.
- We added a dedicated
Using
scene.traversefor Batch Operations- The
cleanupScenefunction shows howscene.traverselets you iterate over every object in the scene. This is perfect for batch tasks like resource cleanup, updating materials, or adjusting properties across multiple meshes.
- The
Proper Material Disposal
- Calling
obj.material.dispose()tells WebGL to release GPU resources tied to that material. This is critical for preventing memory leaks, especially in long-running apps or single-page applications where scenes are swapped out. - Always pair material disposal with geometry disposal (
obj.geometry.dispose()) unless the geometry is reused across multiple meshes.
- Calling
Code Clarity
- Renamed duplicate
objvariables to descriptive names, making the code easier to read and debug. - Ensured bloom and non-bloom objects are positioned correctly to avoid unintended visual overlap.
- Renamed duplicate
内容的提问来源于stack exchange,提问作者Vladimir Fedorov
相关产品推荐
相关产品推荐

