@lark-apaas/coding-steering 0.1.3 → 0.1.4
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/package.json +8 -13
- package/steering/vite-react/skills/react-three-fiber/SKILL.md +214 -0
- package/steering/vite-react/skills/react-three-fiber/references/animation.md +1001 -0
- package/steering/vite-react/skills/react-three-fiber/references/fundamentals.md +877 -0
- package/steering/vite-react/skills/react-three-fiber/references/geometry.md +717 -0
- package/steering/vite-react/skills/react-three-fiber/references/interaction.md +880 -0
- package/steering/vite-react/skills/react-three-fiber/references/lighting.md +668 -0
- package/steering/vite-react/skills/react-three-fiber/references/loaders.md +607 -0
- package/steering/vite-react/skills/react-three-fiber/references/materials.md +601 -0
- package/steering/vite-react/skills/react-three-fiber/references/physics.md +820 -0
- package/steering/vite-react/skills/react-three-fiber/references/postprocessing.md +754 -0
- package/steering/vite-react/skills/react-three-fiber/references/shaders.md +874 -0
- package/steering/vite-react/skills/react-three-fiber/references/textures.md +635 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
# React Three Fiber Post-Processing
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Canvas } from '@react-three/fiber'
|
|
7
|
+
import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing'
|
|
8
|
+
|
|
9
|
+
function Scene() {
|
|
10
|
+
return (
|
|
11
|
+
<Canvas>
|
|
12
|
+
<ambientLight />
|
|
13
|
+
<mesh>
|
|
14
|
+
<boxGeometry />
|
|
15
|
+
<meshStandardMaterial color="hotpink" emissive="hotpink" emissiveIntensity={2} />
|
|
16
|
+
</mesh>
|
|
17
|
+
|
|
18
|
+
<EffectComposer>
|
|
19
|
+
<Bloom luminanceThreshold={0.5} luminanceSmoothing={0.9} intensity={1.5} />
|
|
20
|
+
<Vignette offset={0.5} darkness={0.5} />
|
|
21
|
+
</EffectComposer>
|
|
22
|
+
</Canvas>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @react-three/postprocessing postprocessing
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## EffectComposer
|
|
34
|
+
|
|
35
|
+
The container for all post-processing effects.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { EffectComposer } from '@react-three/postprocessing'
|
|
39
|
+
|
|
40
|
+
function Scene() {
|
|
41
|
+
return (
|
|
42
|
+
<Canvas>
|
|
43
|
+
{/* Scene content */}
|
|
44
|
+
<mesh>...</mesh>
|
|
45
|
+
|
|
46
|
+
{/* Post-processing - must be inside Canvas, after scene content */}
|
|
47
|
+
<EffectComposer
|
|
48
|
+
enabled={true} // Toggle all effects
|
|
49
|
+
depthBuffer={true} // Enable depth buffer
|
|
50
|
+
stencilBuffer={false} // Enable stencil buffer
|
|
51
|
+
autoClear={true} // Auto clear before render
|
|
52
|
+
multisampling={8} // MSAA samples (0 to disable)
|
|
53
|
+
>
|
|
54
|
+
{/* Effects go here */}
|
|
55
|
+
</EffectComposer>
|
|
56
|
+
</Canvas>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Common Effects
|
|
62
|
+
|
|
63
|
+
### Bloom (Glow)
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { EffectComposer, Bloom } from '@react-three/postprocessing'
|
|
67
|
+
import { BlendFunction } from 'postprocessing'
|
|
68
|
+
|
|
69
|
+
<EffectComposer>
|
|
70
|
+
<Bloom
|
|
71
|
+
intensity={1.0} // Bloom intensity
|
|
72
|
+
luminanceThreshold={0.9} // Brightness threshold
|
|
73
|
+
luminanceSmoothing={0.025} // Smoothness of threshold
|
|
74
|
+
mipmapBlur={true} // Enable mipmap blur
|
|
75
|
+
radius={0.8} // Bloom radius
|
|
76
|
+
levels={8} // Mipmap levels
|
|
77
|
+
blendFunction={BlendFunction.ADD}
|
|
78
|
+
/>
|
|
79
|
+
</EffectComposer>
|
|
80
|
+
|
|
81
|
+
// For objects to glow, use emissive materials
|
|
82
|
+
<mesh>
|
|
83
|
+
<boxGeometry />
|
|
84
|
+
<meshStandardMaterial
|
|
85
|
+
color="black"
|
|
86
|
+
emissive="#ff00ff"
|
|
87
|
+
emissiveIntensity={2}
|
|
88
|
+
toneMapped={false} // Important for values > 1
|
|
89
|
+
/>
|
|
90
|
+
</mesh>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Selective Bloom
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { EffectComposer, Bloom, Selection, Select } from '@react-three/postprocessing'
|
|
97
|
+
|
|
98
|
+
function Scene() {
|
|
99
|
+
return (
|
|
100
|
+
<Canvas>
|
|
101
|
+
<Selection>
|
|
102
|
+
<EffectComposer>
|
|
103
|
+
<Bloom
|
|
104
|
+
luminanceThreshold={0}
|
|
105
|
+
intensity={2}
|
|
106
|
+
mipmapBlur
|
|
107
|
+
/>
|
|
108
|
+
</EffectComposer>
|
|
109
|
+
|
|
110
|
+
{/* This mesh will bloom */}
|
|
111
|
+
<Select enabled>
|
|
112
|
+
<mesh>
|
|
113
|
+
<sphereGeometry />
|
|
114
|
+
<meshStandardMaterial emissive="red" emissiveIntensity={2} toneMapped={false} />
|
|
115
|
+
</mesh>
|
|
116
|
+
</Select>
|
|
117
|
+
|
|
118
|
+
{/* This mesh will NOT bloom */}
|
|
119
|
+
<mesh position={[2, 0, 0]}>
|
|
120
|
+
<boxGeometry />
|
|
121
|
+
<meshStandardMaterial color="blue" />
|
|
122
|
+
</mesh>
|
|
123
|
+
</Selection>
|
|
124
|
+
</Canvas>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Depth of Field
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { EffectComposer, DepthOfField } from '@react-three/postprocessing'
|
|
133
|
+
|
|
134
|
+
<EffectComposer>
|
|
135
|
+
<DepthOfField
|
|
136
|
+
focusDistance={0} // Focus distance (0 = auto)
|
|
137
|
+
focalLength={0.02} // Camera focal length
|
|
138
|
+
bokehScale={2} // Bokeh size
|
|
139
|
+
height={480} // Resolution height
|
|
140
|
+
/>
|
|
141
|
+
</EffectComposer>
|
|
142
|
+
|
|
143
|
+
// With target object
|
|
144
|
+
import { useRef } from 'react'
|
|
145
|
+
|
|
146
|
+
function Scene() {
|
|
147
|
+
const targetRef = useRef()
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<>
|
|
151
|
+
<mesh ref={targetRef} position={[0, 0, -5]}>
|
|
152
|
+
<boxGeometry />
|
|
153
|
+
<meshStandardMaterial color="red" />
|
|
154
|
+
</mesh>
|
|
155
|
+
|
|
156
|
+
<EffectComposer>
|
|
157
|
+
<DepthOfField
|
|
158
|
+
target={targetRef}
|
|
159
|
+
focalLength={0.02}
|
|
160
|
+
bokehScale={2}
|
|
161
|
+
/>
|
|
162
|
+
</EffectComposer>
|
|
163
|
+
</>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Vignette
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { EffectComposer, Vignette } from '@react-three/postprocessing'
|
|
172
|
+
|
|
173
|
+
<EffectComposer>
|
|
174
|
+
<Vignette
|
|
175
|
+
offset={0.5} // Vignette size
|
|
176
|
+
darkness={0.5} // Vignette intensity
|
|
177
|
+
eskil={false} // Use Eskil's vignette technique
|
|
178
|
+
/>
|
|
179
|
+
</EffectComposer>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Noise
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { EffectComposer, Noise } from '@react-three/postprocessing'
|
|
186
|
+
import { BlendFunction } from 'postprocessing'
|
|
187
|
+
|
|
188
|
+
<EffectComposer>
|
|
189
|
+
<Noise
|
|
190
|
+
premultiply // Multiply noise with input
|
|
191
|
+
blendFunction={BlendFunction.ADD}
|
|
192
|
+
opacity={0.5}
|
|
193
|
+
/>
|
|
194
|
+
</EffectComposer>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Chromatic Aberration
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { EffectComposer, ChromaticAberration } from '@react-three/postprocessing'
|
|
201
|
+
import { BlendFunction } from 'postprocessing'
|
|
202
|
+
|
|
203
|
+
<EffectComposer>
|
|
204
|
+
<ChromaticAberration
|
|
205
|
+
offset={[0.002, 0.002]} // Color offset
|
|
206
|
+
radialModulation={true} // Apply radially
|
|
207
|
+
modulationOffset={0.5}
|
|
208
|
+
blendFunction={BlendFunction.NORMAL}
|
|
209
|
+
/>
|
|
210
|
+
</EffectComposer>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### SSAO (Screen Space Ambient Occlusion)
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { EffectComposer, SSAO } from '@react-three/postprocessing'
|
|
217
|
+
import { BlendFunction } from 'postprocessing'
|
|
218
|
+
|
|
219
|
+
<EffectComposer>
|
|
220
|
+
<SSAO
|
|
221
|
+
blendFunction={BlendFunction.MULTIPLY}
|
|
222
|
+
samples={30} // Amount of samples
|
|
223
|
+
radius={5} // Occlusion radius
|
|
224
|
+
intensity={30} // Occlusion intensity
|
|
225
|
+
luminanceInfluence={0.6}
|
|
226
|
+
color="black"
|
|
227
|
+
worldDistanceThreshold={100}
|
|
228
|
+
worldDistanceFalloff={5}
|
|
229
|
+
worldProximityThreshold={0.01}
|
|
230
|
+
worldProximityFalloff={0.01}
|
|
231
|
+
/>
|
|
232
|
+
</EffectComposer>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Outline
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { EffectComposer, Outline, Selection, Select } from '@react-three/postprocessing'
|
|
239
|
+
|
|
240
|
+
function Scene() {
|
|
241
|
+
return (
|
|
242
|
+
<Canvas>
|
|
243
|
+
<Selection>
|
|
244
|
+
<EffectComposer autoClear={false}>
|
|
245
|
+
<Outline
|
|
246
|
+
visibleEdgeColor={0xffffff} // Visible edge color
|
|
247
|
+
hiddenEdgeColor={0x22090a} // Hidden edge color
|
|
248
|
+
edgeStrength={2.5} // Edge strength
|
|
249
|
+
pulseSpeed={0} // Pulse animation speed
|
|
250
|
+
blur // Enable blur
|
|
251
|
+
xRay // Show behind objects
|
|
252
|
+
/>
|
|
253
|
+
</EffectComposer>
|
|
254
|
+
|
|
255
|
+
{/* Outlined object */}
|
|
256
|
+
<Select enabled>
|
|
257
|
+
<mesh>
|
|
258
|
+
<boxGeometry />
|
|
259
|
+
<meshStandardMaterial color="orange" />
|
|
260
|
+
</mesh>
|
|
261
|
+
</Select>
|
|
262
|
+
|
|
263
|
+
{/* Non-outlined object */}
|
|
264
|
+
<mesh position={[2, 0, 0]}>
|
|
265
|
+
<sphereGeometry />
|
|
266
|
+
<meshStandardMaterial color="blue" />
|
|
267
|
+
</mesh>
|
|
268
|
+
</Selection>
|
|
269
|
+
</Canvas>
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Color Grading
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
import { EffectComposer, BrightnessContrast, HueSaturation } from '@react-three/postprocessing'
|
|
278
|
+
|
|
279
|
+
<EffectComposer>
|
|
280
|
+
<BrightnessContrast
|
|
281
|
+
brightness={0} // -1 to 1
|
|
282
|
+
contrast={0} // -1 to 1
|
|
283
|
+
/>
|
|
284
|
+
<HueSaturation
|
|
285
|
+
hue={0} // Hue rotation in radians
|
|
286
|
+
saturation={0} // -1 to 1
|
|
287
|
+
/>
|
|
288
|
+
</EffectComposer>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Tone Mapping
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
import { EffectComposer, ToneMapping } from '@react-three/postprocessing'
|
|
295
|
+
import { ToneMappingMode } from 'postprocessing'
|
|
296
|
+
|
|
297
|
+
<EffectComposer>
|
|
298
|
+
<ToneMapping
|
|
299
|
+
mode={ToneMappingMode.ACES_FILMIC}
|
|
300
|
+
// Modes: LINEAR, REINHARD, REINHARD2, OPTIMIZED_CINEON, CINEON, ACES_FILMIC, AGX, NEUTRAL
|
|
301
|
+
resolution={256}
|
|
302
|
+
whitePoint={4.0}
|
|
303
|
+
middleGrey={0.6}
|
|
304
|
+
minLuminance={0.01}
|
|
305
|
+
averageLuminance={1.0}
|
|
306
|
+
adaptationRate={1.0}
|
|
307
|
+
/>
|
|
308
|
+
</EffectComposer>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Glitch
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import { EffectComposer, Glitch } from '@react-three/postprocessing'
|
|
315
|
+
import { GlitchMode } from 'postprocessing'
|
|
316
|
+
|
|
317
|
+
<EffectComposer>
|
|
318
|
+
<Glitch
|
|
319
|
+
delay={[1.5, 3.5]} // Min/max delay between glitches
|
|
320
|
+
duration={[0.6, 1.0]} // Min/max duration
|
|
321
|
+
strength={[0.3, 1.0]} // Min/max strength
|
|
322
|
+
mode={GlitchMode.SPORADIC} // DISABLED, SPORADIC, CONSTANT_MILD, CONSTANT_WILD
|
|
323
|
+
active // Enable/disable
|
|
324
|
+
ratio={0.85} // Glitch ratio (0 = none, 1 = always)
|
|
325
|
+
/>
|
|
326
|
+
</EffectComposer>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Pixelation
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
import { EffectComposer, Pixelation } from '@react-three/postprocessing'
|
|
333
|
+
|
|
334
|
+
<EffectComposer>
|
|
335
|
+
<Pixelation
|
|
336
|
+
granularity={5} // Pixel size
|
|
337
|
+
/>
|
|
338
|
+
</EffectComposer>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Scanline
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
import { EffectComposer, Scanline } from '@react-three/postprocessing'
|
|
345
|
+
import { BlendFunction } from 'postprocessing'
|
|
346
|
+
|
|
347
|
+
<EffectComposer>
|
|
348
|
+
<Scanline
|
|
349
|
+
blendFunction={BlendFunction.OVERLAY}
|
|
350
|
+
density={1.25} // Line density
|
|
351
|
+
opacity={0.5} // Effect opacity
|
|
352
|
+
/>
|
|
353
|
+
</EffectComposer>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Grid
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
import { EffectComposer, Grid } from '@react-three/postprocessing'
|
|
360
|
+
import { BlendFunction } from 'postprocessing'
|
|
361
|
+
|
|
362
|
+
<EffectComposer>
|
|
363
|
+
<Grid
|
|
364
|
+
blendFunction={BlendFunction.OVERLAY}
|
|
365
|
+
scale={1.0} // Grid scale
|
|
366
|
+
lineWidth={0.0} // Line width
|
|
367
|
+
/>
|
|
368
|
+
</EffectComposer>
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### DotScreen
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
import { EffectComposer, DotScreen } from '@react-three/postprocessing'
|
|
375
|
+
import { BlendFunction } from 'postprocessing'
|
|
376
|
+
|
|
377
|
+
<EffectComposer>
|
|
378
|
+
<DotScreen
|
|
379
|
+
blendFunction={BlendFunction.NORMAL}
|
|
380
|
+
angle={Math.PI * 0.5} // Pattern angle
|
|
381
|
+
scale={1.0} // Pattern scale
|
|
382
|
+
/>
|
|
383
|
+
</EffectComposer>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### SMAA (Anti-Aliasing)
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import { EffectComposer, SMAA } from '@react-three/postprocessing'
|
|
390
|
+
|
|
391
|
+
<EffectComposer multisampling={0}> {/* Disable MSAA when using SMAA */}
|
|
392
|
+
<SMAA />
|
|
393
|
+
</EffectComposer>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### FXAA (Anti-Aliasing)
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
import { EffectComposer, FXAA } from '@react-three/postprocessing'
|
|
400
|
+
|
|
401
|
+
<EffectComposer multisampling={0}>
|
|
402
|
+
<FXAA />
|
|
403
|
+
</EffectComposer>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Combining Multiple Effects
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
import { EffectComposer, Bloom, Vignette, ChromaticAberration, Noise, SMAA } from '@react-three/postprocessing'
|
|
410
|
+
import { BlendFunction } from 'postprocessing'
|
|
411
|
+
|
|
412
|
+
function PostProcessing() {
|
|
413
|
+
return (
|
|
414
|
+
<EffectComposer multisampling={0}>
|
|
415
|
+
{/* Glow effect */}
|
|
416
|
+
<Bloom
|
|
417
|
+
intensity={1.5}
|
|
418
|
+
luminanceThreshold={0.9}
|
|
419
|
+
luminanceSmoothing={0.025}
|
|
420
|
+
mipmapBlur
|
|
421
|
+
/>
|
|
422
|
+
|
|
423
|
+
{/* Color aberration */}
|
|
424
|
+
<ChromaticAberration
|
|
425
|
+
offset={[0.001, 0.001]}
|
|
426
|
+
radialModulation
|
|
427
|
+
modulationOffset={0.5}
|
|
428
|
+
/>
|
|
429
|
+
|
|
430
|
+
{/* Film grain */}
|
|
431
|
+
<Noise
|
|
432
|
+
premultiply
|
|
433
|
+
blendFunction={BlendFunction.ADD}
|
|
434
|
+
opacity={0.2}
|
|
435
|
+
/>
|
|
436
|
+
|
|
437
|
+
{/* Vignette */}
|
|
438
|
+
<Vignette
|
|
439
|
+
offset={0.3}
|
|
440
|
+
darkness={0.5}
|
|
441
|
+
/>
|
|
442
|
+
|
|
443
|
+
{/* Anti-aliasing (should be last) */}
|
|
444
|
+
<SMAA />
|
|
445
|
+
</EffectComposer>
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Custom Effects
|
|
451
|
+
|
|
452
|
+
### Using postprocessing Effect Class
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
import { forwardRef, useMemo } from 'react'
|
|
456
|
+
import { Effect, BlendFunction } from 'postprocessing'
|
|
457
|
+
import { Uniform } from 'three'
|
|
458
|
+
|
|
459
|
+
// Fragment shader
|
|
460
|
+
const fragmentShader = `
|
|
461
|
+
uniform float time;
|
|
462
|
+
uniform float intensity;
|
|
463
|
+
|
|
464
|
+
void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
|
|
465
|
+
vec2 distortedUv = uv;
|
|
466
|
+
distortedUv.x += sin(uv.y * 10.0 + time) * 0.01 * intensity;
|
|
467
|
+
|
|
468
|
+
vec4 color = texture2D(inputBuffer, distortedUv);
|
|
469
|
+
outputColor = color;
|
|
470
|
+
}
|
|
471
|
+
`
|
|
472
|
+
|
|
473
|
+
// Effect class
|
|
474
|
+
class WaveDistortionEffect extends Effect {
|
|
475
|
+
constructor({ intensity = 1.0, blendFunction = BlendFunction.NORMAL } = {}) {
|
|
476
|
+
super('WaveDistortionEffect', fragmentShader, {
|
|
477
|
+
blendFunction,
|
|
478
|
+
uniforms: new Map([
|
|
479
|
+
['time', new Uniform(0)],
|
|
480
|
+
['intensity', new Uniform(intensity)],
|
|
481
|
+
]),
|
|
482
|
+
})
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
update(renderer, inputBuffer, deltaTime) {
|
|
486
|
+
this.uniforms.get('time').value += deltaTime
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// React component wrapper
|
|
491
|
+
export const WaveDistortion = forwardRef(({ intensity = 1.0, blendFunction }, ref) => {
|
|
492
|
+
const effect = useMemo(() => new WaveDistortionEffect({ intensity, blendFunction }), [intensity, blendFunction])
|
|
493
|
+
return <primitive ref={ref} object={effect} dispose={null} />
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
// Usage
|
|
497
|
+
<EffectComposer>
|
|
498
|
+
<WaveDistortion intensity={0.5} />
|
|
499
|
+
</EffectComposer>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Shader with wrapEffect
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
import { wrapEffect } from '@react-three/postprocessing'
|
|
506
|
+
import { Effect, BlendFunction } from 'postprocessing'
|
|
507
|
+
import { Uniform } from 'three'
|
|
508
|
+
|
|
509
|
+
class InvertEffect extends Effect {
|
|
510
|
+
constructor({ blendFunction = BlendFunction.NORMAL } = {}) {
|
|
511
|
+
super('InvertEffect', `
|
|
512
|
+
void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
|
|
513
|
+
outputColor = vec4(1.0 - inputColor.rgb, inputColor.a);
|
|
514
|
+
}
|
|
515
|
+
`, {
|
|
516
|
+
blendFunction,
|
|
517
|
+
})
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export const Invert = wrapEffect(InvertEffect)
|
|
522
|
+
|
|
523
|
+
// Usage
|
|
524
|
+
<EffectComposer>
|
|
525
|
+
<Invert />
|
|
526
|
+
</EffectComposer>
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Conditional Effects
|
|
530
|
+
|
|
531
|
+
```tsx
|
|
532
|
+
import { useState } from 'react'
|
|
533
|
+
import { EffectComposer, Bloom, Vignette, Glitch } from '@react-three/postprocessing'
|
|
534
|
+
|
|
535
|
+
function ConditionalPostProcessing() {
|
|
536
|
+
const [effects, setEffects] = useState({
|
|
537
|
+
bloom: true,
|
|
538
|
+
vignette: true,
|
|
539
|
+
glitch: false,
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
return (
|
|
543
|
+
<>
|
|
544
|
+
<EffectComposer>
|
|
545
|
+
{effects.bloom && (
|
|
546
|
+
<Bloom intensity={1.5} luminanceThreshold={0.9} />
|
|
547
|
+
)}
|
|
548
|
+
{effects.vignette && (
|
|
549
|
+
<Vignette offset={0.5} darkness={0.5} />
|
|
550
|
+
)}
|
|
551
|
+
{effects.glitch && (
|
|
552
|
+
<Glitch delay={[1, 3]} duration={[0.5, 1]} strength={[0.3, 1]} />
|
|
553
|
+
)}
|
|
554
|
+
</EffectComposer>
|
|
555
|
+
|
|
556
|
+
{/* UI to toggle effects */}
|
|
557
|
+
<div className="controls">
|
|
558
|
+
<button onClick={() => setEffects(e => ({ ...e, bloom: !e.bloom }))}>
|
|
559
|
+
Toggle Bloom
|
|
560
|
+
</button>
|
|
561
|
+
<button onClick={() => setEffects(e => ({ ...e, vignette: !e.vignette }))}>
|
|
562
|
+
Toggle Vignette
|
|
563
|
+
</button>
|
|
564
|
+
<button onClick={() => setEffects(e => ({ ...e, glitch: !e.glitch }))}>
|
|
565
|
+
Toggle Glitch
|
|
566
|
+
</button>
|
|
567
|
+
</div>
|
|
568
|
+
</>
|
|
569
|
+
)
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Animated Effects
|
|
574
|
+
|
|
575
|
+
```tsx
|
|
576
|
+
import { useRef } from 'react'
|
|
577
|
+
import { useFrame } from '@react-three/fiber'
|
|
578
|
+
import { EffectComposer, Bloom, ChromaticAberration } from '@react-three/postprocessing'
|
|
579
|
+
|
|
580
|
+
function AnimatedEffects() {
|
|
581
|
+
const bloomRef = useRef()
|
|
582
|
+
const chromaticRef = useRef()
|
|
583
|
+
|
|
584
|
+
useFrame(({ clock }) => {
|
|
585
|
+
const t = clock.elapsedTime
|
|
586
|
+
|
|
587
|
+
// Animate bloom intensity
|
|
588
|
+
if (bloomRef.current) {
|
|
589
|
+
bloomRef.current.intensity = 1 + Math.sin(t) * 0.5
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Animate chromatic aberration
|
|
593
|
+
if (chromaticRef.current) {
|
|
594
|
+
const offset = Math.sin(t * 2) * 0.002
|
|
595
|
+
chromaticRef.current.offset.set(offset, offset)
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
return (
|
|
600
|
+
<EffectComposer>
|
|
601
|
+
<Bloom ref={bloomRef} luminanceThreshold={0.9} />
|
|
602
|
+
<ChromaticAberration ref={chromaticRef} />
|
|
603
|
+
</EffectComposer>
|
|
604
|
+
)
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## N8AO (High Quality AO)
|
|
609
|
+
|
|
610
|
+
```tsx
|
|
611
|
+
import { EffectComposer } from '@react-three/postprocessing'
|
|
612
|
+
import { N8AO } from '@react-three/postprocessing'
|
|
613
|
+
|
|
614
|
+
<EffectComposer>
|
|
615
|
+
<N8AO
|
|
616
|
+
aoRadius={0.5}
|
|
617
|
+
intensity={1}
|
|
618
|
+
aoSamples={6}
|
|
619
|
+
denoiseSamples={4}
|
|
620
|
+
denoiseRadius={12}
|
|
621
|
+
distanceFalloff={1}
|
|
622
|
+
color="black"
|
|
623
|
+
quality="low" // low, medium, high, ultra
|
|
624
|
+
halfRes={false}
|
|
625
|
+
/>
|
|
626
|
+
</EffectComposer>
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## God Rays
|
|
630
|
+
|
|
631
|
+
```tsx
|
|
632
|
+
import { EffectComposer, GodRays } from '@react-three/postprocessing'
|
|
633
|
+
import { useRef } from 'react'
|
|
634
|
+
|
|
635
|
+
function Scene() {
|
|
636
|
+
const sunRef = useRef()
|
|
637
|
+
|
|
638
|
+
return (
|
|
639
|
+
<Canvas>
|
|
640
|
+
{/* Sun mesh (light source for god rays) */}
|
|
641
|
+
<mesh ref={sunRef} position={[0, 5, -10]}>
|
|
642
|
+
<sphereGeometry args={[1]} />
|
|
643
|
+
<meshBasicMaterial color="#ffddaa" />
|
|
644
|
+
</mesh>
|
|
645
|
+
|
|
646
|
+
<EffectComposer>
|
|
647
|
+
{sunRef.current && (
|
|
648
|
+
<GodRays
|
|
649
|
+
sun={sunRef}
|
|
650
|
+
blendFunction={BlendFunction.SCREEN}
|
|
651
|
+
samples={60}
|
|
652
|
+
density={0.96}
|
|
653
|
+
decay={0.9}
|
|
654
|
+
weight={0.4}
|
|
655
|
+
exposure={0.6}
|
|
656
|
+
clampMax={1}
|
|
657
|
+
blur
|
|
658
|
+
/>
|
|
659
|
+
)}
|
|
660
|
+
</EffectComposer>
|
|
661
|
+
</Canvas>
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
## LUT (Color Lookup Table)
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
import { EffectComposer, LUT } from '@react-three/postprocessing'
|
|
670
|
+
import { LUTCubeLoader } from 'postprocessing'
|
|
671
|
+
import { useLoader } from '@react-three/fiber'
|
|
672
|
+
|
|
673
|
+
function ColorGradedScene() {
|
|
674
|
+
const texture = useLoader(LUTCubeLoader, '/luts/cinematic.cube')
|
|
675
|
+
|
|
676
|
+
return (
|
|
677
|
+
<EffectComposer>
|
|
678
|
+
<LUT lut={texture} />
|
|
679
|
+
</EffectComposer>
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
## Blend Functions
|
|
685
|
+
|
|
686
|
+
```tsx
|
|
687
|
+
import { BlendFunction } from 'postprocessing'
|
|
688
|
+
|
|
689
|
+
// Available blend functions:
|
|
690
|
+
BlendFunction.SKIP // Skip blending
|
|
691
|
+
BlendFunction.ADD // Additive
|
|
692
|
+
BlendFunction.ALPHA // Alpha
|
|
693
|
+
BlendFunction.AVERAGE // Average
|
|
694
|
+
BlendFunction.COLOR // Color
|
|
695
|
+
BlendFunction.COLOR_BURN // Color Burn
|
|
696
|
+
BlendFunction.COLOR_DODGE // Color Dodge
|
|
697
|
+
BlendFunction.DARKEN // Darken
|
|
698
|
+
BlendFunction.DIFFERENCE // Difference
|
|
699
|
+
BlendFunction.DIVIDE // Divide
|
|
700
|
+
BlendFunction.DST // Destination
|
|
701
|
+
BlendFunction.EXCLUSION // Exclusion
|
|
702
|
+
BlendFunction.HARD_LIGHT // Hard Light
|
|
703
|
+
BlendFunction.HARD_MIX // Hard Mix
|
|
704
|
+
BlendFunction.HUE // Hue
|
|
705
|
+
BlendFunction.INVERT // Invert
|
|
706
|
+
BlendFunction.INVERT_RGB // Invert RGB
|
|
707
|
+
BlendFunction.LIGHTEN // Lighten
|
|
708
|
+
BlendFunction.LINEAR_BURN // Linear Burn
|
|
709
|
+
BlendFunction.LINEAR_DODGE // Linear Dodge
|
|
710
|
+
BlendFunction.LINEAR_LIGHT // Linear Light
|
|
711
|
+
BlendFunction.LUMINOSITY // Luminosity
|
|
712
|
+
BlendFunction.MULTIPLY // Multiply
|
|
713
|
+
BlendFunction.NEGATION // Negation
|
|
714
|
+
BlendFunction.NORMAL // Normal
|
|
715
|
+
BlendFunction.OVERLAY // Overlay
|
|
716
|
+
BlendFunction.PIN_LIGHT // Pin Light
|
|
717
|
+
BlendFunction.REFLECT // Reflect
|
|
718
|
+
BlendFunction.SATURATION // Saturation
|
|
719
|
+
BlendFunction.SCREEN // Screen
|
|
720
|
+
BlendFunction.SET // Set
|
|
721
|
+
BlendFunction.SOFT_LIGHT // Soft Light
|
|
722
|
+
BlendFunction.SRC // Source
|
|
723
|
+
BlendFunction.SUBTRACT // Subtract
|
|
724
|
+
BlendFunction.VIVID_LIGHT // Vivid Light
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
## Performance Tips
|
|
728
|
+
|
|
729
|
+
1. **Limit effect count**: Each effect adds rendering overhead
|
|
730
|
+
2. **Use multisampling wisely**: Higher values = slower performance
|
|
731
|
+
3. **Disable when not needed**: Toggle `enabled` prop
|
|
732
|
+
4. **Lower resolution**: Some effects have resolution props
|
|
733
|
+
5. **Profile with DevTools**: Monitor GPU usage
|
|
734
|
+
|
|
735
|
+
```tsx
|
|
736
|
+
// Disable all effects
|
|
737
|
+
<EffectComposer enabled={performanceMode}>
|
|
738
|
+
...
|
|
739
|
+
</EffectComposer>
|
|
740
|
+
|
|
741
|
+
// Reduce effect quality on mobile
|
|
742
|
+
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent)
|
|
743
|
+
|
|
744
|
+
<EffectComposer multisampling={isMobile ? 0 : 8}>
|
|
745
|
+
<Bloom mipmapBlur={!isMobile} radius={isMobile ? 0.4 : 0.8} />
|
|
746
|
+
{!isMobile && <SSAO samples={16} />}
|
|
747
|
+
</EffectComposer>
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
## See Also
|
|
751
|
+
|
|
752
|
+
- `r3f-shaders` - Custom shader development
|
|
753
|
+
- `r3f-materials` - Emissive materials for bloom
|
|
754
|
+
- `r3f-fundamentals` - Canvas and renderer setup
|