ThreeJS 使用发射贴图对一个对象的特定部分进行选择性绽放
ThreeJS Selective Bloom For Specific Parts Of One Object Using Emission Map
在我的项目中,我想显示有时带有小 LED 灯的 3d 对象。这个想法是,这些小灯需要发出某种光晕,使它们看起来像是在发光。
我已尝试应用 UnrealBloom,但它被认为适用于整个场景,而不仅仅是具有实际发射值的部分(使用发射纹理贴图)。场景也变得非常模糊。
这显然不是我想要的。我只需要红色的小 LED 灯泡来点亮整个物体。然而,我还没有找到一种方法来告诉引擎只将光晕应用到排放贴图指向的地方。
我使用的代码设置非常简单,几乎与 UnrealBloom Example:
相同
如何正确设置发射纹理并仅使物体的发射部分发光并防止不真实的闪亮表面和非常模糊的视觉效果?
UPDATE: Editable example of my setup is now available on JSFiddle!
<body style="margin:0px; overflow:hidden;">
<div id="bloom-solution">
<div id="body">
<h2 id="info" style="
color: rgb(255,255,255);
position: fixed;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
">loading scene, this might take a few seconds..</h2>
<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">
import * as THREE from 'https://threejs.org/build/three.module.js'
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js'
import { RGBELoader } from 'https://threejs.org/examples/jsm/loaders/RGBELoader.js'
import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'https://threejs.org/examples/jsm/postprocessing/UnrealBloomPass.js';
// RESOURCES ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
const COLOR_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Color.jpeg"
const METALNESS_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Metalness.jpeg"
const EMISSION_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Emission.jpeg"
const ALPHA_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Alpha.jpeg"
const TURNTABLE_MODEL = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/turntable_a111.glb"
const HDRI_MAP = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/forest.hdr"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function $(e){return document.getElementById(e)}
const container = document.createElement( 'div' )
document.body.appendChild( container )
const scene = new THREE.Scene()
scene.background = new THREE.Color( new THREE.Color("rgb(250,244,227)") )
scene.fog = new THREE.Fog( new THREE.Color("rgb(100, 100, 100)"), 10, 50 )
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 )
camera.position.set( 7, 3, 7 )
const renderer = new THREE.WebGLRenderer( { antialias: true } )
renderer.setPixelRatio( window.devicePixelRatio )
renderer.setSize( window.innerWidth, window.innerHeight )
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.outputEncoding = THREE.sRGBEncoding
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
container.appendChild( renderer.domElement )
const controls = new OrbitControls( camera, renderer.domElement )
controls.minDistance = 1
controls.enablePan = true
controls.enableZoom = true;
controls.enableDamping = true
controls.dampingFactor = 0.1
controls.rotateSpeed = 0.5
const directionalLight = new THREE.DirectionalLight( new THREE.Color("rgb(255, 255, 255)"), 1 )
directionalLight.castShadow = true
directionalLight.shadow.camera.top = 4
directionalLight.shadow.camera.bottom = - 4
directionalLight.shadow.camera.left = - 4
directionalLight.shadow.camera.right = 4
directionalLight.shadow.camera.near = 0.1
directionalLight.shadow.camera.far = 40
directionalLight.shadow.camera.far = 40
directionalLight.shadow.bias = - 0.002
directionalLight.position.set( 0, 20, 20 )
directionalLight.shadow.mapSize.width = 1024*4
directionalLight.shadow.mapSize.height = 1024*4
scene.add( directionalLight )
scene.add( new THREE.CameraHelper( directionalLight.shadow.camera ) )
var gltfLoader
var model
var mesh
const pmremGenerator = new THREE.PMREMGenerator( renderer )
pmremGenerator.compileEquirectangularShader()
new RGBELoader().setDataType( THREE.UnsignedByteType ).load( HDRI_MAP, function ( texture ) {
const envMap = pmremGenerator.fromEquirectangular( texture ).texture
scene.environment = envMap
texture.dispose()
pmremGenerator.dispose()
gltfLoader = new GLTFLoader()
gltfLoader.load( TURNTABLE_MODEL, function ( gltf ) {
model = gltf.scene
model.position.y = 1
model.traverse( function ( child ) {
if ( child.isMesh ) {
mesh = child
child.castShadow = true
child.receiveShadow = true
child.material.transparent = true
child.material.envMapIntensity = 1
$("info").style.display = "none";
}
} );
model.scale.set(15,15,15)
scene.add( model )
animate()
} )
});
const animate = function () {
requestAnimationFrame( animate )
controls.update()
renderer.render( scene, camera )
};
window.addEventListener( 'resize', function () {
const width = window.innerWidth
const height = window.innerHeight
renderer.setSize( width, height )
camera.aspect = width / height
camera.updateProjectionMatrix()
} )
</script>
</div>
</div>
</body>
在我看来,这个官方示例过于复杂。但是选择性绽放的概念本身很简单:
- 使所有非泛光对象完全变黑
- 使用
bloomComposer
渲染场景
- 恢复materials/colors到以前的
- 使用
finalComposer
渲染场景
就是这样。如何管理darkening/blackening非开花对象并恢复其材质,由您决定。
举个例子(看似复杂,其实没那么复杂):
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">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.127.0/build/three.module.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 scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 3, 5);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let uniforms = {
globalBloom: {value: 1}
}
// texture
new THREE.TextureLoader().load("https://threejs.org/examples/textures/hardwood2_diffuse.jpg", tex => {
//console.log(tex);
let img = tex.image;
let c = document.createElement("canvas");
let min = Math.min(img.width, img.height);
c.width = c.height = min;
let ctx = c.getContext("2d");
ctx.drawImage(img, 0, 0);
let c2 = document.createElement("canvas");
c2.width = c2.height = min;
let ctx2 = c2.getContext("2d");
ctx2.clearRect(0, 0, min, min);
["#f00", "#0f0", "#ff0", "#f0f", "#0ff"].forEach( (col, i, a) => {
let id = i - ((a.length - 1) / 2);
let dist = id * 150;
//console.log(dist, col, i, c.width, c.height);
ctx.beginPath();
ctx.arc(min * 0.5 + dist, min * 0.5, 25, 0, 2 * Math.PI);
ctx.fillStyle = col;
ctx.fill();
}
);
let cTex = new THREE.CanvasTexture(c);
let c2Tex = new THREE.CanvasTexture(c2);
setInterval(() => {
ctx2.clearRect(0, 0, min, min);
let id = THREE.MathUtils.randInt(0, 4) - 2;
let dist = id * 150;
ctx2.beginPath();
ctx2.arc(min * 0.5 + dist, min * 0.5, 25, 0, 2 * Math.PI);
ctx2.fillStyle = "#fff";
ctx2.fill();
c2Tex.needsUpdate = true;
}, 125);
let g = new THREE.PlaneGeometry(5, 5);
g.rotateX(Math.PI * -0.5);
let m = new THREE.MeshStandardMaterial(
{
roughness: 0.6,
metalness: 0.5,
map: cTex,
emissiveMap: c2Tex,
onBeforeCompile: shader => {
shader.uniforms.globalBloom = uniforms.globalBloom;
shader.fragmentShader = `
uniform float globalBloom;
${shader.fragmentShader}
`.replace(
`#include <dithering_fragment>`,
`#include <dithering_fragment>
vec3 col = texture2D( map, vUv).rgb;
float em = texture2D( emissiveMap, vUv ).g;
col *= em;
gl_FragColor.rgb = mix(gl_FragColor.rgb, col, globalBloom);
`
);
console.log(shader.fragmentShader);
}
}
);
let o = new THREE.Mesh(g, m);
scene.add(o);
})
window.onresize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
bloomComposer.setSize( width, height );
finalComposer.setSize( width, height );
};
// bloom
const renderScene = new RenderPass( scene, camera );
const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0, 0.1 );
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 );
renderer.setAnimationLoop( _ => {
renderer.setClearColor(0x000000);
uniforms.globalBloom.value = 1;
bloomComposer.render();
renderer.setClearColor(0x202020);
uniforms.globalBloom.value = 0;
finalComposer.render();
//renderer.render(scene, camera);
})
</script>
有一种方法,比完成所有这些复杂的步骤和遍历要简单得多。只需将颜色推到正常的 0-1 范围之外,就是这样:https://twitter.com/0xca0a/status/1525083552672632833
- 将 bloom 设置为阈值 1(不开花)
- 将 material 颜色推入高清范围
- 禁用色调映射
在我的项目中,我想显示有时带有小 LED 灯的 3d 对象。这个想法是,这些小灯需要发出某种光晕,使它们看起来像是在发光。
我已尝试应用 UnrealBloom,但它被认为适用于整个场景,而不仅仅是具有实际发射值的部分(使用发射纹理贴图)。场景也变得非常模糊。
这显然不是我想要的。我只需要红色的小 LED 灯泡来点亮整个物体。然而,我还没有找到一种方法来告诉引擎只将光晕应用到排放贴图指向的地方。
我使用的代码设置非常简单,几乎与 UnrealBloom Example:
相同如何正确设置发射纹理并仅使物体的发射部分发光并防止不真实的闪亮表面和非常模糊的视觉效果?
UPDATE: Editable example of my setup is now available on JSFiddle!
<body style="margin:0px; overflow:hidden;">
<div id="bloom-solution">
<div id="body">
<h2 id="info" style="
color: rgb(255,255,255);
position: fixed;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
">loading scene, this might take a few seconds..</h2>
<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">
import * as THREE from 'https://threejs.org/build/three.module.js'
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js'
import { RGBELoader } from 'https://threejs.org/examples/jsm/loaders/RGBELoader.js'
import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'https://threejs.org/examples/jsm/postprocessing/UnrealBloomPass.js';
// RESOURCES ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
const COLOR_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Color.jpeg"
const METALNESS_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Metalness.jpeg"
const EMISSION_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Emission.jpeg"
const ALPHA_TEXTURE = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/RecordPlayer_Alpha.jpeg"
const TURNTABLE_MODEL = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/turntable_a111.glb"
const HDRI_MAP = "https://cdn.jsdelivr.net/gh/MigerRepo/bloom-solution/forest.hdr"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function $(e){return document.getElementById(e)}
const container = document.createElement( 'div' )
document.body.appendChild( container )
const scene = new THREE.Scene()
scene.background = new THREE.Color( new THREE.Color("rgb(250,244,227)") )
scene.fog = new THREE.Fog( new THREE.Color("rgb(100, 100, 100)"), 10, 50 )
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 )
camera.position.set( 7, 3, 7 )
const renderer = new THREE.WebGLRenderer( { antialias: true } )
renderer.setPixelRatio( window.devicePixelRatio )
renderer.setSize( window.innerWidth, window.innerHeight )
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.outputEncoding = THREE.sRGBEncoding
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
container.appendChild( renderer.domElement )
const controls = new OrbitControls( camera, renderer.domElement )
controls.minDistance = 1
controls.enablePan = true
controls.enableZoom = true;
controls.enableDamping = true
controls.dampingFactor = 0.1
controls.rotateSpeed = 0.5
const directionalLight = new THREE.DirectionalLight( new THREE.Color("rgb(255, 255, 255)"), 1 )
directionalLight.castShadow = true
directionalLight.shadow.camera.top = 4
directionalLight.shadow.camera.bottom = - 4
directionalLight.shadow.camera.left = - 4
directionalLight.shadow.camera.right = 4
directionalLight.shadow.camera.near = 0.1
directionalLight.shadow.camera.far = 40
directionalLight.shadow.camera.far = 40
directionalLight.shadow.bias = - 0.002
directionalLight.position.set( 0, 20, 20 )
directionalLight.shadow.mapSize.width = 1024*4
directionalLight.shadow.mapSize.height = 1024*4
scene.add( directionalLight )
scene.add( new THREE.CameraHelper( directionalLight.shadow.camera ) )
var gltfLoader
var model
var mesh
const pmremGenerator = new THREE.PMREMGenerator( renderer )
pmremGenerator.compileEquirectangularShader()
new RGBELoader().setDataType( THREE.UnsignedByteType ).load( HDRI_MAP, function ( texture ) {
const envMap = pmremGenerator.fromEquirectangular( texture ).texture
scene.environment = envMap
texture.dispose()
pmremGenerator.dispose()
gltfLoader = new GLTFLoader()
gltfLoader.load( TURNTABLE_MODEL, function ( gltf ) {
model = gltf.scene
model.position.y = 1
model.traverse( function ( child ) {
if ( child.isMesh ) {
mesh = child
child.castShadow = true
child.receiveShadow = true
child.material.transparent = true
child.material.envMapIntensity = 1
$("info").style.display = "none";
}
} );
model.scale.set(15,15,15)
scene.add( model )
animate()
} )
});
const animate = function () {
requestAnimationFrame( animate )
controls.update()
renderer.render( scene, camera )
};
window.addEventListener( 'resize', function () {
const width = window.innerWidth
const height = window.innerHeight
renderer.setSize( width, height )
camera.aspect = width / height
camera.updateProjectionMatrix()
} )
</script>
</div>
</div>
</body>
在我看来,这个官方示例过于复杂。但是选择性绽放的概念本身很简单:
- 使所有非泛光对象完全变黑
- 使用
bloomComposer
渲染场景
- 恢复materials/colors到以前的
- 使用
finalComposer
渲染场景
就是这样。如何管理darkening/blackening非开花对象并恢复其材质,由您决定。
举个例子(看似复杂,其实没那么复杂):
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">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.127.0/build/three.module.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 scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 3, 5);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
let uniforms = {
globalBloom: {value: 1}
}
// texture
new THREE.TextureLoader().load("https://threejs.org/examples/textures/hardwood2_diffuse.jpg", tex => {
//console.log(tex);
let img = tex.image;
let c = document.createElement("canvas");
let min = Math.min(img.width, img.height);
c.width = c.height = min;
let ctx = c.getContext("2d");
ctx.drawImage(img, 0, 0);
let c2 = document.createElement("canvas");
c2.width = c2.height = min;
let ctx2 = c2.getContext("2d");
ctx2.clearRect(0, 0, min, min);
["#f00", "#0f0", "#ff0", "#f0f", "#0ff"].forEach( (col, i, a) => {
let id = i - ((a.length - 1) / 2);
let dist = id * 150;
//console.log(dist, col, i, c.width, c.height);
ctx.beginPath();
ctx.arc(min * 0.5 + dist, min * 0.5, 25, 0, 2 * Math.PI);
ctx.fillStyle = col;
ctx.fill();
}
);
let cTex = new THREE.CanvasTexture(c);
let c2Tex = new THREE.CanvasTexture(c2);
setInterval(() => {
ctx2.clearRect(0, 0, min, min);
let id = THREE.MathUtils.randInt(0, 4) - 2;
let dist = id * 150;
ctx2.beginPath();
ctx2.arc(min * 0.5 + dist, min * 0.5, 25, 0, 2 * Math.PI);
ctx2.fillStyle = "#fff";
ctx2.fill();
c2Tex.needsUpdate = true;
}, 125);
let g = new THREE.PlaneGeometry(5, 5);
g.rotateX(Math.PI * -0.5);
let m = new THREE.MeshStandardMaterial(
{
roughness: 0.6,
metalness: 0.5,
map: cTex,
emissiveMap: c2Tex,
onBeforeCompile: shader => {
shader.uniforms.globalBloom = uniforms.globalBloom;
shader.fragmentShader = `
uniform float globalBloom;
${shader.fragmentShader}
`.replace(
`#include <dithering_fragment>`,
`#include <dithering_fragment>
vec3 col = texture2D( map, vUv).rgb;
float em = texture2D( emissiveMap, vUv ).g;
col *= em;
gl_FragColor.rgb = mix(gl_FragColor.rgb, col, globalBloom);
`
);
console.log(shader.fragmentShader);
}
}
);
let o = new THREE.Mesh(g, m);
scene.add(o);
})
window.onresize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
bloomComposer.setSize( width, height );
finalComposer.setSize( width, height );
};
// bloom
const renderScene = new RenderPass( scene, camera );
const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0, 0.1 );
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 );
renderer.setAnimationLoop( _ => {
renderer.setClearColor(0x000000);
uniforms.globalBloom.value = 1;
bloomComposer.render();
renderer.setClearColor(0x202020);
uniforms.globalBloom.value = 0;
finalComposer.render();
//renderer.render(scene, camera);
})
</script>
有一种方法,比完成所有这些复杂的步骤和遍历要简单得多。只需将颜色推到正常的 0-1 范围之外,就是这样:https://twitter.com/0xca0a/status/1525083552672632833
- 将 bloom 设置为阈值 1(不开花)
- 将 material 颜色推入高清范围
- 禁用色调映射