Skip to content

Commit 8bc4338

Browse files
committed
Implement selective bloom
1 parent 3f59058 commit 8bc4338

File tree

2 files changed

+55
-40
lines changed

2 files changed

+55
-40
lines changed

src/game/scene-elements.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import {
66
Scene,
77
SRGBColorSpace,
88
TextureLoader,
9-
Vector2,
109
WebGLRenderer,
11-
WebGLRenderTarget
1210
} from "three";
13-
import { EffectComposer, HDRLoader, RenderPass, UnrealBloomPass } from "three/examples/jsm/Addons.js";
11+
import { HDRLoader } from "three/examples/jsm/Addons.js";
1412
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
1513
import { buildPrefilteredRadianceMap } from "../loaders";
1614

@@ -54,28 +52,4 @@ export async function createBackground(scene: Scene, renderer: WebGLRenderer): P
5452
const backgroundMap = await new TextureLoader().loadAsync('/hdri/rich_multi_nebulae_2k.png');
5553
backgroundMap.colorSpace = SRGBColorSpace;
5654
scene.background = (await buildPrefilteredRadianceMap(backgroundMap, renderer)).texture;
57-
}
58-
59-
export function createGlowEffect(scene: Scene, camera: Camera, renderer: WebGLRenderer)
60-
: EffectComposer {
61-
62-
const w = renderer.domElement.clientWidth;
63-
const h = renderer.domElement.clientHeight;
64-
65-
const bloom = new UnrealBloomPass(
66-
new Vector2(w, h),
67-
/*strength*/1.2,
68-
/*radius*/0.4,
69-
/*threshold*/0.85
70-
);
71-
72-
const rt = new WebGLRenderTarget(w, h, {
73-
samples: Math.min(4, renderer.capabilities.maxSamples)
74-
});
75-
76-
const composer = new EffectComposer(renderer, rt);
77-
composer.addPass(new RenderPass(scene, camera));
78-
composer.addPass(bloom);
79-
80-
return composer;
8155
}

src/main.ts

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {
1010
createCamera,
1111
createControls,
1212
createBackground,
13-
createLightsAsync,
14-
createGlowEffect
13+
createLightsAsync
1514
} from "./game/scene-elements";
1615
import {
1716
Clock,
@@ -35,6 +34,8 @@ import { localPlayer } from "./game/player";
3534
import GUI from 'lil-gui';
3635
import { World } from "./game/world";
3736
import { create8x8BoardAsync } from "./game/board";
37+
import { EffectComposer, OutputPass, RenderPass, ShaderPass, UnrealBloomPass } from "three/examples/jsm/Addons.js";
38+
import { BloomManager } from "./game/bloom-manager";
3839

3940
// Required for Github Pages deployment
4041
DefaultLoadingManager.setURLModifier((url) => {
@@ -106,17 +107,56 @@ async function init() {
106107
const scene = new Scene();
107108
const camera = createCamera(w, h);
108109
const controls = createControls(camera, renderer);
109-
let composer = isMobile() ?
110-
undefined :
111-
createGlowEffect(scene, camera, renderer)
110+
111+
const bloomStrength = 0.6;
112+
const bloomTarget = new WebGLRenderTarget(w, h, {
113+
samples: Math.min(4, renderer.capabilities.maxSamples),
114+
type: HalfFloatType
115+
});
116+
const bloomComposer = new EffectComposer(renderer, bloomTarget);
117+
bloomComposer.renderToScreen = false;
118+
bloomComposer.addPass(new RenderPass(scene, camera));
119+
bloomComposer.addPass(new UnrealBloomPass(new Vector2(w, h), bloomStrength, 0.4, 0.85));
120+
121+
const renderTarget = new WebGLRenderTarget(w, h, {
122+
samples: Math.min(4, renderer.capabilities.maxSamples),
123+
type: HalfFloatType
124+
});
125+
const finalComposer = new EffectComposer(renderer, renderTarget);
126+
const addBloomPass = new ShaderPass(new ShaderMaterial({
127+
uniforms: {
128+
baseTexture: { value: null }, // set by ShaderPass
129+
bloomTexture: { value: bloomComposer.renderTarget2.texture },
130+
bloomStrength: { value: bloomStrength }
131+
},
132+
vertexShader: `
133+
varying vec2 vUv;
134+
void main() { vUv = uv; gl_Position = vec4(position,1.0); }
135+
`,
136+
fragmentShader: `
137+
uniform sampler2D baseTexture, bloomTexture;
138+
uniform float bloomStrength;
139+
varying vec2 vUv;
140+
void main() {
141+
vec4 base = texture2D(baseTexture, vUv);
142+
vec4 bloom = texture2D(bloomTexture, vUv);
143+
gl_FragColor = base + bloom * bloomStrength;
144+
}
145+
`
146+
}), 'baseTexture');
147+
148+
finalComposer.addPass(new RenderPass(scene, camera));
149+
finalComposer.addPass(addBloomPass);
150+
finalComposer.addPass(new OutputPass());
112151

113152
window.addEventListener("resize", () => {
114153
let w = canvas.clientWidth;
115154
let h = canvas.clientHeight;
116155
camera.aspect = w / h;
117156
camera.updateProjectionMatrix();
118157
renderer.setSize(w, h);
119-
composer?.setSize(w, h);
158+
bloomComposer?.setSize(w, h);
159+
finalComposer?.setSize(w, h);
120160
});
121161

122162
await createBackground(scene, renderer);
@@ -138,17 +178,18 @@ async function init() {
138178
}
139179

140180
const clock = new Clock();
141-
181+
const bloomManager = new BloomManager(scene);
142182
function Update() {
143183
const delta = clock.getDelta();
144184
world.update(delta)
145185
controls.update();
146-
if (composer) {
147-
composer.render();
148-
}
149-
else {
150-
renderer.render(scene, camera);
151-
}
186+
187+
bloomManager.overrideMaterialsToBlack();
188+
bloomComposer.render();
189+
bloomManager.restoreMaterials();
190+
191+
finalComposer.render();
192+
152193
renderFps(delta);
153194

154195
requestAnimationFrame(Update);

0 commit comments

Comments
 (0)