create-threejs-game 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/bin/cli.js +370 -0
- package/package.json +29 -0
- package/template/.claude/skills/threejs-animation/SKILL.md +552 -0
- package/template/.claude/skills/threejs-fundamentals/SKILL.md +488 -0
- package/template/.claude/skills/threejs-geometry/SKILL.md +548 -0
- package/template/.claude/skills/threejs-interaction/SKILL.md +660 -0
- package/template/.claude/skills/threejs-lighting/SKILL.md +481 -0
- package/template/.claude/skills/threejs-loaders/SKILL.md +623 -0
- package/template/.claude/skills/threejs-materials/SKILL.md +520 -0
- package/template/.claude/skills/threejs-postprocessing/SKILL.md +602 -0
- package/template/.claude/skills/threejs-shaders/SKILL.md +642 -0
- package/template/.claude/skills/threejs-textures/SKILL.md +628 -0
- package/template/.codex/skills/threejs-animation/SKILL.md +552 -0
- package/template/.codex/skills/threejs-fundamentals/SKILL.md +488 -0
- package/template/.codex/skills/threejs-geometry/SKILL.md +548 -0
- package/template/.codex/skills/threejs-interaction/SKILL.md +660 -0
- package/template/.codex/skills/threejs-lighting/SKILL.md +481 -0
- package/template/.codex/skills/threejs-loaders/SKILL.md +623 -0
- package/template/.codex/skills/threejs-materials/SKILL.md +520 -0
- package/template/.codex/skills/threejs-postprocessing/SKILL.md +602 -0
- package/template/.codex/skills/threejs-shaders/SKILL.md +642 -0
- package/template/.codex/skills/threejs-textures/SKILL.md +628 -0
- package/template/README.md +352 -0
- package/template/docs/.gitkeep +0 -0
- package/template/plans/.gitkeep +0 -0
- package/template/prompts/01-mockup-generation.md +44 -0
- package/template/prompts/02-prd-generation.md +119 -0
- package/template/prompts/03-tdd-generation.md +127 -0
- package/template/prompts/04-execution-plan.md +89 -0
- package/template/prompts/05-implementation.md +61 -0
- package/template/public/assets/.gitkeep +0 -0
- package/template/scripts/config.example.json +12 -0
- package/template/scripts/generate-assets-json.js +149 -0
- package/template/scripts/generate-mockup.js +197 -0
- package/template/scripts/generate-plan.js +295 -0
- package/template/scripts/generate-prd.js +297 -0
- package/template/scripts/generate-tdd.js +283 -0
- package/template/scripts/pipeline.js +229 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: threejs-postprocessing
|
|
3
|
+
description: Three.js post-processing - EffectComposer, bloom, DOF, screen effects. Use when adding visual effects, color grading, blur, glow, or creating custom screen-space shaders.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Three.js Post-Processing
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
import * as THREE from "three";
|
|
12
|
+
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
|
|
13
|
+
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
|
14
|
+
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
|
|
15
|
+
|
|
16
|
+
// Setup composer
|
|
17
|
+
const composer = new EffectComposer(renderer);
|
|
18
|
+
|
|
19
|
+
// Render scene
|
|
20
|
+
const renderPass = new RenderPass(scene, camera);
|
|
21
|
+
composer.addPass(renderPass);
|
|
22
|
+
|
|
23
|
+
// Add bloom
|
|
24
|
+
const bloomPass = new UnrealBloomPass(
|
|
25
|
+
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
|
26
|
+
1.5, // strength
|
|
27
|
+
0.4, // radius
|
|
28
|
+
0.85, // threshold
|
|
29
|
+
);
|
|
30
|
+
composer.addPass(bloomPass);
|
|
31
|
+
|
|
32
|
+
// Animation loop - use composer instead of renderer
|
|
33
|
+
function animate() {
|
|
34
|
+
requestAnimationFrame(animate);
|
|
35
|
+
composer.render(); // NOT renderer.render()
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## EffectComposer Setup
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
|
|
43
|
+
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
|
44
|
+
|
|
45
|
+
const composer = new EffectComposer(renderer);
|
|
46
|
+
|
|
47
|
+
// First pass: render scene
|
|
48
|
+
const renderPass = new RenderPass(scene, camera);
|
|
49
|
+
composer.addPass(renderPass);
|
|
50
|
+
|
|
51
|
+
// Add more passes...
|
|
52
|
+
composer.addPass(effectPass);
|
|
53
|
+
|
|
54
|
+
// Last pass should render to screen
|
|
55
|
+
effectPass.renderToScreen = true; // Default for last pass
|
|
56
|
+
|
|
57
|
+
// Handle resize
|
|
58
|
+
function onResize() {
|
|
59
|
+
const width = window.innerWidth;
|
|
60
|
+
const height = window.innerHeight;
|
|
61
|
+
|
|
62
|
+
camera.aspect = width / height;
|
|
63
|
+
camera.updateProjectionMatrix();
|
|
64
|
+
|
|
65
|
+
renderer.setSize(width, height);
|
|
66
|
+
composer.setSize(width, height);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Common Effects
|
|
71
|
+
|
|
72
|
+
### Bloom (Glow)
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
|
|
76
|
+
|
|
77
|
+
const bloomPass = new UnrealBloomPass(
|
|
78
|
+
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
|
79
|
+
1.5, // strength - intensity of glow
|
|
80
|
+
0.4, // radius - spread of glow
|
|
81
|
+
0.85, // threshold - brightness threshold
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
composer.addPass(bloomPass);
|
|
85
|
+
|
|
86
|
+
// Adjust at runtime
|
|
87
|
+
bloomPass.strength = 2.0;
|
|
88
|
+
bloomPass.threshold = 0.5;
|
|
89
|
+
bloomPass.radius = 0.8;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Selective Bloom
|
|
93
|
+
|
|
94
|
+
Apply bloom only to specific objects.
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
|
|
98
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
99
|
+
|
|
100
|
+
// Layer setup
|
|
101
|
+
const BLOOM_LAYER = 1;
|
|
102
|
+
const bloomLayer = new THREE.Layers();
|
|
103
|
+
bloomLayer.set(BLOOM_LAYER);
|
|
104
|
+
|
|
105
|
+
// Mark objects to bloom
|
|
106
|
+
glowingMesh.layers.enable(BLOOM_LAYER);
|
|
107
|
+
|
|
108
|
+
// Dark material for non-blooming objects
|
|
109
|
+
const darkMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
|
|
110
|
+
const materials = {};
|
|
111
|
+
|
|
112
|
+
function darkenNonBloomed(obj) {
|
|
113
|
+
if (obj.isMesh && !bloomLayer.test(obj.layers)) {
|
|
114
|
+
materials[obj.uuid] = obj.material;
|
|
115
|
+
obj.material = darkMaterial;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function restoreMaterial(obj) {
|
|
120
|
+
if (materials[obj.uuid]) {
|
|
121
|
+
obj.material = materials[obj.uuid];
|
|
122
|
+
delete materials[obj.uuid];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Custom render loop
|
|
127
|
+
function render() {
|
|
128
|
+
// Render bloom pass
|
|
129
|
+
scene.traverse(darkenNonBloomed);
|
|
130
|
+
composer.render();
|
|
131
|
+
scene.traverse(restoreMaterial);
|
|
132
|
+
|
|
133
|
+
// Render final scene over bloom
|
|
134
|
+
renderer.render(scene, camera);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### FXAA (Anti-Aliasing)
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
142
|
+
import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
|
|
143
|
+
|
|
144
|
+
const fxaaPass = new ShaderPass(FXAAShader);
|
|
145
|
+
fxaaPass.material.uniforms["resolution"].value.set(
|
|
146
|
+
1 / window.innerWidth,
|
|
147
|
+
1 / window.innerHeight,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
composer.addPass(fxaaPass);
|
|
151
|
+
|
|
152
|
+
// Update on resize
|
|
153
|
+
function onResize() {
|
|
154
|
+
fxaaPass.material.uniforms["resolution"].value.set(
|
|
155
|
+
1 / window.innerWidth,
|
|
156
|
+
1 / window.innerHeight,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### SMAA (Better Anti-Aliasing)
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
import { SMAAPass } from "three/addons/postprocessing/SMAAPass.js";
|
|
165
|
+
|
|
166
|
+
const smaaPass = new SMAAPass(
|
|
167
|
+
window.innerWidth * renderer.getPixelRatio(),
|
|
168
|
+
window.innerHeight * renderer.getPixelRatio(),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
composer.addPass(smaaPass);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### SSAO (Ambient Occlusion)
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
import { SSAOPass } from "three/addons/postprocessing/SSAOPass.js";
|
|
178
|
+
|
|
179
|
+
const ssaoPass = new SSAOPass(
|
|
180
|
+
scene,
|
|
181
|
+
camera,
|
|
182
|
+
window.innerWidth,
|
|
183
|
+
window.innerHeight,
|
|
184
|
+
);
|
|
185
|
+
ssaoPass.kernelRadius = 16;
|
|
186
|
+
ssaoPass.minDistance = 0.005;
|
|
187
|
+
ssaoPass.maxDistance = 0.1;
|
|
188
|
+
|
|
189
|
+
composer.addPass(ssaoPass);
|
|
190
|
+
|
|
191
|
+
// Output modes
|
|
192
|
+
ssaoPass.output = SSAOPass.OUTPUT.Default;
|
|
193
|
+
// SSAOPass.OUTPUT.Default - Final composited output
|
|
194
|
+
// SSAOPass.OUTPUT.SSAO - Just the AO
|
|
195
|
+
// SSAOPass.OUTPUT.Blur - Blurred AO
|
|
196
|
+
// SSAOPass.OUTPUT.Depth - Depth buffer
|
|
197
|
+
// SSAOPass.OUTPUT.Normal - Normal buffer
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Depth of Field (DOF)
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
import { BokehPass } from "three/addons/postprocessing/BokehPass.js";
|
|
204
|
+
|
|
205
|
+
const bokehPass = new BokehPass(scene, camera, {
|
|
206
|
+
focus: 10.0, // Focus distance
|
|
207
|
+
aperture: 0.025, // Aperture (smaller = more DOF)
|
|
208
|
+
maxblur: 0.01, // Max blur amount
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
composer.addPass(bokehPass);
|
|
212
|
+
|
|
213
|
+
// Update focus dynamically
|
|
214
|
+
bokehPass.uniforms["focus"].value = distanceToTarget;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Film Grain
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
import { FilmPass } from "three/addons/postprocessing/FilmPass.js";
|
|
221
|
+
|
|
222
|
+
const filmPass = new FilmPass(
|
|
223
|
+
0.35, // noise intensity
|
|
224
|
+
0.5, // scanline intensity
|
|
225
|
+
648, // scanline count
|
|
226
|
+
false, // grayscale
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
composer.addPass(filmPass);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Vignette
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
236
|
+
import { VignetteShader } from "three/addons/shaders/VignetteShader.js";
|
|
237
|
+
|
|
238
|
+
const vignettePass = new ShaderPass(VignetteShader);
|
|
239
|
+
vignettePass.uniforms["offset"].value = 1.0; // Vignette size
|
|
240
|
+
vignettePass.uniforms["darkness"].value = 1.0; // Vignette intensity
|
|
241
|
+
|
|
242
|
+
composer.addPass(vignettePass);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Color Correction
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
249
|
+
import { ColorCorrectionShader } from "three/addons/shaders/ColorCorrectionShader.js";
|
|
250
|
+
|
|
251
|
+
const colorPass = new ShaderPass(ColorCorrectionShader);
|
|
252
|
+
colorPass.uniforms["powRGB"].value = new THREE.Vector3(1.2, 1.2, 1.2); // Power
|
|
253
|
+
colorPass.uniforms["mulRGB"].value = new THREE.Vector3(1.0, 1.0, 1.0); // Multiply
|
|
254
|
+
|
|
255
|
+
composer.addPass(colorPass);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Gamma Correction
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
import { GammaCorrectionShader } from "three/addons/shaders/GammaCorrectionShader.js";
|
|
262
|
+
|
|
263
|
+
const gammaPass = new ShaderPass(GammaCorrectionShader);
|
|
264
|
+
composer.addPass(gammaPass);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Pixelation
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
import { RenderPixelatedPass } from "three/addons/postprocessing/RenderPixelatedPass.js";
|
|
271
|
+
|
|
272
|
+
const pixelPass = new RenderPixelatedPass(6, scene, camera); // 6 = pixel size
|
|
273
|
+
|
|
274
|
+
composer.addPass(pixelPass);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Glitch Effect
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
import { GlitchPass } from "three/addons/postprocessing/GlitchPass.js";
|
|
281
|
+
|
|
282
|
+
const glitchPass = new GlitchPass();
|
|
283
|
+
glitchPass.goWild = false; // Continuous glitching
|
|
284
|
+
|
|
285
|
+
composer.addPass(glitchPass);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Halftone
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
import { HalftonePass } from "three/addons/postprocessing/HalftonePass.js";
|
|
292
|
+
|
|
293
|
+
const halftonePass = new HalftonePass(window.innerWidth, window.innerHeight, {
|
|
294
|
+
shape: 1, // 1 = dot, 2 = ellipse, 3 = line, 4 = square
|
|
295
|
+
radius: 4, // Dot size
|
|
296
|
+
rotateR: Math.PI / 12,
|
|
297
|
+
rotateB: (Math.PI / 12) * 2,
|
|
298
|
+
rotateG: (Math.PI / 12) * 3,
|
|
299
|
+
scatter: 0,
|
|
300
|
+
blending: 1,
|
|
301
|
+
blendingMode: 1,
|
|
302
|
+
greyscale: false,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
composer.addPass(halftonePass);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Outline
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
import { OutlinePass } from "three/addons/postprocessing/OutlinePass.js";
|
|
312
|
+
|
|
313
|
+
const outlinePass = new OutlinePass(
|
|
314
|
+
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
|
315
|
+
scene,
|
|
316
|
+
camera,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
outlinePass.edgeStrength = 3;
|
|
320
|
+
outlinePass.edgeGlow = 0;
|
|
321
|
+
outlinePass.edgeThickness = 1;
|
|
322
|
+
outlinePass.pulsePeriod = 0;
|
|
323
|
+
outlinePass.visibleEdgeColor.set(0xffffff);
|
|
324
|
+
outlinePass.hiddenEdgeColor.set(0x190a05);
|
|
325
|
+
|
|
326
|
+
// Select objects to outline
|
|
327
|
+
outlinePass.selectedObjects = [mesh1, mesh2];
|
|
328
|
+
|
|
329
|
+
composer.addPass(outlinePass);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Custom ShaderPass
|
|
333
|
+
|
|
334
|
+
Create your own post-processing effects.
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
338
|
+
|
|
339
|
+
const CustomShader = {
|
|
340
|
+
uniforms: {
|
|
341
|
+
tDiffuse: { value: null }, // Required: input texture
|
|
342
|
+
time: { value: 0 },
|
|
343
|
+
intensity: { value: 1.0 },
|
|
344
|
+
},
|
|
345
|
+
vertexShader: `
|
|
346
|
+
varying vec2 vUv;
|
|
347
|
+
|
|
348
|
+
void main() {
|
|
349
|
+
vUv = uv;
|
|
350
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
351
|
+
}
|
|
352
|
+
`,
|
|
353
|
+
fragmentShader: `
|
|
354
|
+
uniform sampler2D tDiffuse;
|
|
355
|
+
uniform float time;
|
|
356
|
+
uniform float intensity;
|
|
357
|
+
varying vec2 vUv;
|
|
358
|
+
|
|
359
|
+
void main() {
|
|
360
|
+
vec2 uv = vUv;
|
|
361
|
+
|
|
362
|
+
// Wave distortion
|
|
363
|
+
uv.x += sin(uv.y * 10.0 + time) * 0.01 * intensity;
|
|
364
|
+
|
|
365
|
+
vec4 color = texture2D(tDiffuse, uv);
|
|
366
|
+
gl_FragColor = color;
|
|
367
|
+
}
|
|
368
|
+
`,
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const customPass = new ShaderPass(CustomShader);
|
|
372
|
+
composer.addPass(customPass);
|
|
373
|
+
|
|
374
|
+
// Update in animation loop
|
|
375
|
+
customPass.uniforms.time.value = clock.getElapsedTime();
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Invert Colors Shader
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
const InvertShader = {
|
|
382
|
+
uniforms: {
|
|
383
|
+
tDiffuse: { value: null },
|
|
384
|
+
},
|
|
385
|
+
vertexShader: `
|
|
386
|
+
varying vec2 vUv;
|
|
387
|
+
void main() {
|
|
388
|
+
vUv = uv;
|
|
389
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
390
|
+
}
|
|
391
|
+
`,
|
|
392
|
+
fragmentShader: `
|
|
393
|
+
uniform sampler2D tDiffuse;
|
|
394
|
+
varying vec2 vUv;
|
|
395
|
+
|
|
396
|
+
void main() {
|
|
397
|
+
vec4 color = texture2D(tDiffuse, vUv);
|
|
398
|
+
gl_FragColor = vec4(1.0 - color.rgb, color.a);
|
|
399
|
+
}
|
|
400
|
+
`,
|
|
401
|
+
};
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Chromatic Aberration
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
const ChromaticAberrationShader = {
|
|
408
|
+
uniforms: {
|
|
409
|
+
tDiffuse: { value: null },
|
|
410
|
+
amount: { value: 0.005 },
|
|
411
|
+
},
|
|
412
|
+
vertexShader: `
|
|
413
|
+
varying vec2 vUv;
|
|
414
|
+
void main() {
|
|
415
|
+
vUv = uv;
|
|
416
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
417
|
+
}
|
|
418
|
+
`,
|
|
419
|
+
fragmentShader: `
|
|
420
|
+
uniform sampler2D tDiffuse;
|
|
421
|
+
uniform float amount;
|
|
422
|
+
varying vec2 vUv;
|
|
423
|
+
|
|
424
|
+
void main() {
|
|
425
|
+
vec2 dir = vUv - 0.5;
|
|
426
|
+
float dist = length(dir);
|
|
427
|
+
|
|
428
|
+
float r = texture2D(tDiffuse, vUv - dir * amount * dist).r;
|
|
429
|
+
float g = texture2D(tDiffuse, vUv).g;
|
|
430
|
+
float b = texture2D(tDiffuse, vUv + dir * amount * dist).b;
|
|
431
|
+
|
|
432
|
+
gl_FragColor = vec4(r, g, b, 1.0);
|
|
433
|
+
}
|
|
434
|
+
`,
|
|
435
|
+
};
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Combining Multiple Effects
|
|
439
|
+
|
|
440
|
+
```javascript
|
|
441
|
+
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
|
|
442
|
+
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
|
443
|
+
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
|
|
444
|
+
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
|
|
445
|
+
import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
|
|
446
|
+
import { VignetteShader } from "three/addons/shaders/VignetteShader.js";
|
|
447
|
+
import { GammaCorrectionShader } from "three/addons/shaders/GammaCorrectionShader.js";
|
|
448
|
+
|
|
449
|
+
const composer = new EffectComposer(renderer);
|
|
450
|
+
|
|
451
|
+
// 1. Render scene
|
|
452
|
+
composer.addPass(new RenderPass(scene, camera));
|
|
453
|
+
|
|
454
|
+
// 2. Bloom
|
|
455
|
+
const bloomPass = new UnrealBloomPass(
|
|
456
|
+
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
|
457
|
+
0.5,
|
|
458
|
+
0.4,
|
|
459
|
+
0.85,
|
|
460
|
+
);
|
|
461
|
+
composer.addPass(bloomPass);
|
|
462
|
+
|
|
463
|
+
// 3. Vignette
|
|
464
|
+
const vignettePass = new ShaderPass(VignetteShader);
|
|
465
|
+
vignettePass.uniforms["offset"].value = 0.95;
|
|
466
|
+
vignettePass.uniforms["darkness"].value = 1.0;
|
|
467
|
+
composer.addPass(vignettePass);
|
|
468
|
+
|
|
469
|
+
// 4. Gamma correction
|
|
470
|
+
composer.addPass(new ShaderPass(GammaCorrectionShader));
|
|
471
|
+
|
|
472
|
+
// 5. Anti-aliasing (always last before output)
|
|
473
|
+
const fxaaPass = new ShaderPass(FXAAShader);
|
|
474
|
+
fxaaPass.uniforms["resolution"].value.set(
|
|
475
|
+
1 / window.innerWidth,
|
|
476
|
+
1 / window.innerHeight,
|
|
477
|
+
);
|
|
478
|
+
composer.addPass(fxaaPass);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Render to Texture
|
|
482
|
+
|
|
483
|
+
```javascript
|
|
484
|
+
// Create render target
|
|
485
|
+
const renderTarget = new THREE.WebGLRenderTarget(512, 512);
|
|
486
|
+
|
|
487
|
+
// Render scene to target
|
|
488
|
+
renderer.setRenderTarget(renderTarget);
|
|
489
|
+
renderer.render(scene, camera);
|
|
490
|
+
renderer.setRenderTarget(null);
|
|
491
|
+
|
|
492
|
+
// Use texture
|
|
493
|
+
const texture = renderTarget.texture;
|
|
494
|
+
otherMaterial.map = texture;
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Multi-Pass Rendering
|
|
498
|
+
|
|
499
|
+
```javascript
|
|
500
|
+
// Multiple composers for different scenes/layers
|
|
501
|
+
const bgComposer = new EffectComposer(renderer);
|
|
502
|
+
bgComposer.addPass(new RenderPass(bgScene, camera));
|
|
503
|
+
|
|
504
|
+
const fgComposer = new EffectComposer(renderer);
|
|
505
|
+
fgComposer.addPass(new RenderPass(fgScene, camera));
|
|
506
|
+
fgComposer.addPass(bloomPass);
|
|
507
|
+
|
|
508
|
+
// Combine in render loop
|
|
509
|
+
function animate() {
|
|
510
|
+
// Render background without clearing
|
|
511
|
+
renderer.autoClear = false;
|
|
512
|
+
renderer.clear();
|
|
513
|
+
|
|
514
|
+
bgComposer.render();
|
|
515
|
+
|
|
516
|
+
// Render foreground over it
|
|
517
|
+
renderer.clearDepth();
|
|
518
|
+
fgComposer.render();
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## WebGPU Post-Processing (Three.js r150+)
|
|
523
|
+
|
|
524
|
+
```javascript
|
|
525
|
+
import { postProcessing } from "three/addons/nodes/Nodes.js";
|
|
526
|
+
import { pass, bloom, dof } from "three/addons/nodes/Nodes.js";
|
|
527
|
+
|
|
528
|
+
// Using node-based system
|
|
529
|
+
const scenePass = pass(scene, camera);
|
|
530
|
+
const bloomNode = bloom(scenePass, 0.5, 0.4, 0.85);
|
|
531
|
+
|
|
532
|
+
const postProcessing = new THREE.PostProcessing(renderer);
|
|
533
|
+
postProcessing.outputNode = bloomNode;
|
|
534
|
+
|
|
535
|
+
// Render
|
|
536
|
+
function animate() {
|
|
537
|
+
postProcessing.render();
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Performance Tips
|
|
542
|
+
|
|
543
|
+
1. **Limit passes**: Each pass adds a full-screen render
|
|
544
|
+
2. **Lower resolution**: Use smaller render targets for blur passes
|
|
545
|
+
3. **Disable unused effects**: Toggle passes on/off
|
|
546
|
+
4. **Use FXAA over MSAA**: Less expensive anti-aliasing
|
|
547
|
+
5. **Profile with DevTools**: Check GPU usage
|
|
548
|
+
|
|
549
|
+
```javascript
|
|
550
|
+
// Disable pass
|
|
551
|
+
bloomPass.enabled = false;
|
|
552
|
+
|
|
553
|
+
// Reduce bloom resolution
|
|
554
|
+
const bloomPass = new UnrealBloomPass(
|
|
555
|
+
new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2),
|
|
556
|
+
strength,
|
|
557
|
+
radius,
|
|
558
|
+
threshold,
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
// Only apply effects in high-performance scenarios
|
|
562
|
+
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
|
|
563
|
+
if (!isMobile) {
|
|
564
|
+
composer.addPass(expensivePass);
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
## Handle Resize
|
|
569
|
+
|
|
570
|
+
```javascript
|
|
571
|
+
function onWindowResize() {
|
|
572
|
+
const width = window.innerWidth;
|
|
573
|
+
const height = window.innerHeight;
|
|
574
|
+
const pixelRatio = renderer.getPixelRatio();
|
|
575
|
+
|
|
576
|
+
camera.aspect = width / height;
|
|
577
|
+
camera.updateProjectionMatrix();
|
|
578
|
+
|
|
579
|
+
renderer.setSize(width, height);
|
|
580
|
+
composer.setSize(width, height);
|
|
581
|
+
|
|
582
|
+
// Update pass-specific resolutions
|
|
583
|
+
if (fxaaPass) {
|
|
584
|
+
fxaaPass.material.uniforms["resolution"].value.set(
|
|
585
|
+
1 / (width * pixelRatio),
|
|
586
|
+
1 / (height * pixelRatio),
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (bloomPass) {
|
|
591
|
+
bloomPass.resolution.set(width, height);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
window.addEventListener("resize", onWindowResize);
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
## See Also
|
|
599
|
+
|
|
600
|
+
- `threejs-shaders` - Custom shader development
|
|
601
|
+
- `threejs-textures` - Render targets
|
|
602
|
+
- `threejs-fundamentals` - Renderer setup
|