@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,874 @@
|
|
|
1
|
+
# React Three Fiber Shaders
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Canvas, useFrame, extend } from '@react-three/fiber'
|
|
7
|
+
import { shaderMaterial } from '@react-three/drei'
|
|
8
|
+
import { useRef } from 'react'
|
|
9
|
+
import * as THREE from 'three'
|
|
10
|
+
|
|
11
|
+
// Create custom shader material
|
|
12
|
+
const ColorShiftMaterial = shaderMaterial(
|
|
13
|
+
// Uniforms
|
|
14
|
+
{ time: 0, color: new THREE.Color(0.2, 0.0, 0.1) },
|
|
15
|
+
// Vertex shader
|
|
16
|
+
`
|
|
17
|
+
varying vec2 vUv;
|
|
18
|
+
void main() {
|
|
19
|
+
vUv = uv;
|
|
20
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
21
|
+
}
|
|
22
|
+
`,
|
|
23
|
+
// Fragment shader
|
|
24
|
+
`
|
|
25
|
+
uniform float time;
|
|
26
|
+
uniform vec3 color;
|
|
27
|
+
varying vec2 vUv;
|
|
28
|
+
void main() {
|
|
29
|
+
gl_FragColor = vec4(vUv.x + sin(time), vUv.y + cos(time), color.b, 1.0);
|
|
30
|
+
}
|
|
31
|
+
`
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// Extend so it can be used as JSX
|
|
35
|
+
extend({ ColorShiftMaterial })
|
|
36
|
+
|
|
37
|
+
function ShaderMesh() {
|
|
38
|
+
const materialRef = useRef()
|
|
39
|
+
|
|
40
|
+
useFrame(({ clock }) => {
|
|
41
|
+
materialRef.current.time = clock.elapsedTime
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<mesh>
|
|
46
|
+
<planeGeometry args={[2, 2]} />
|
|
47
|
+
{/* key={Material.key} enables HMR for shader development */}
|
|
48
|
+
<colorShiftMaterial ref={materialRef} key={ColorShiftMaterial.key} />
|
|
49
|
+
</mesh>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default function App() {
|
|
54
|
+
return (
|
|
55
|
+
<Canvas>
|
|
56
|
+
<ShaderMesh />
|
|
57
|
+
</Canvas>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## shaderMaterial (Drei)
|
|
63
|
+
|
|
64
|
+
The recommended way to create shader materials in R3F.
|
|
65
|
+
|
|
66
|
+
### Basic Pattern
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { shaderMaterial } from '@react-three/drei'
|
|
70
|
+
import { extend } from '@react-three/fiber'
|
|
71
|
+
import * as THREE from 'three'
|
|
72
|
+
|
|
73
|
+
// 1. Define the material
|
|
74
|
+
const MyShaderMaterial = shaderMaterial(
|
|
75
|
+
// Uniforms object
|
|
76
|
+
{
|
|
77
|
+
time: 0,
|
|
78
|
+
color: new THREE.Color(1, 0, 0),
|
|
79
|
+
opacity: 1,
|
|
80
|
+
map: null,
|
|
81
|
+
},
|
|
82
|
+
// Vertex shader (GLSL)
|
|
83
|
+
`
|
|
84
|
+
varying vec2 vUv;
|
|
85
|
+
varying vec3 vPosition;
|
|
86
|
+
|
|
87
|
+
void main() {
|
|
88
|
+
vUv = uv;
|
|
89
|
+
vPosition = position;
|
|
90
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
91
|
+
}
|
|
92
|
+
`,
|
|
93
|
+
// Fragment shader (GLSL)
|
|
94
|
+
`
|
|
95
|
+
uniform float time;
|
|
96
|
+
uniform vec3 color;
|
|
97
|
+
uniform float opacity;
|
|
98
|
+
uniform sampler2D map;
|
|
99
|
+
varying vec2 vUv;
|
|
100
|
+
|
|
101
|
+
void main() {
|
|
102
|
+
vec4 texColor = texture2D(map, vUv);
|
|
103
|
+
gl_FragColor = vec4(color * texColor.rgb, opacity);
|
|
104
|
+
}
|
|
105
|
+
`
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
// 2. Extend R3F
|
|
109
|
+
extend({ MyShaderMaterial })
|
|
110
|
+
|
|
111
|
+
// 3. Use in component
|
|
112
|
+
function MyMesh() {
|
|
113
|
+
const materialRef = useRef()
|
|
114
|
+
|
|
115
|
+
useFrame(({ clock }) => {
|
|
116
|
+
materialRef.current.time = clock.elapsedTime
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<mesh>
|
|
121
|
+
<boxGeometry />
|
|
122
|
+
{/* key prop enables Hot Module Replacement during development */}
|
|
123
|
+
<myShaderMaterial
|
|
124
|
+
ref={materialRef}
|
|
125
|
+
key={MyShaderMaterial.key}
|
|
126
|
+
color="hotpink"
|
|
127
|
+
transparent
|
|
128
|
+
opacity={0.8}
|
|
129
|
+
/>
|
|
130
|
+
</mesh>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Hot Module Replacement (HMR)
|
|
136
|
+
|
|
137
|
+
The `key` prop on shaderMaterial enables live shader editing without page refresh:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
const MyMaterial = shaderMaterial(
|
|
141
|
+
{ time: 0 },
|
|
142
|
+
vertexShader,
|
|
143
|
+
fragmentShader
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
extend({ MyMaterial })
|
|
147
|
+
|
|
148
|
+
// MyMaterial.key changes when shader code changes
|
|
149
|
+
<myMaterial key={MyMaterial.key} />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When you edit shader code, the material automatically updates. Without `key`, you'd need to refresh the page to see changes.
|
|
153
|
+
|
|
154
|
+
### TypeScript Support
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { shaderMaterial } from '@react-three/drei'
|
|
158
|
+
import { extend, Object3DNode } from '@react-three/fiber'
|
|
159
|
+
import * as THREE from 'three'
|
|
160
|
+
|
|
161
|
+
// Define uniform types
|
|
162
|
+
type WaveMaterialUniforms = {
|
|
163
|
+
time: number
|
|
164
|
+
amplitude: number
|
|
165
|
+
color: THREE.Color
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const WaveMaterial = shaderMaterial(
|
|
169
|
+
{
|
|
170
|
+
time: 0,
|
|
171
|
+
amplitude: 0.5,
|
|
172
|
+
color: new THREE.Color('hotpink'),
|
|
173
|
+
} as WaveMaterialUniforms,
|
|
174
|
+
// vertex shader
|
|
175
|
+
`...`,
|
|
176
|
+
// fragment shader
|
|
177
|
+
`...`
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
// Extend with proper types
|
|
181
|
+
extend({ WaveMaterial })
|
|
182
|
+
|
|
183
|
+
// Declare for TypeScript
|
|
184
|
+
declare module '@react-three/fiber' {
|
|
185
|
+
interface ThreeElements {
|
|
186
|
+
waveMaterial: Object3DNode<
|
|
187
|
+
typeof WaveMaterial & THREE.ShaderMaterial,
|
|
188
|
+
typeof WaveMaterial
|
|
189
|
+
>
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Raw THREE.ShaderMaterial
|
|
195
|
+
|
|
196
|
+
For full control without Drei helper.
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import { useFrame } from '@react-three/fiber'
|
|
200
|
+
import { useMemo, useRef } from 'react'
|
|
201
|
+
import * as THREE from 'three'
|
|
202
|
+
|
|
203
|
+
function CustomShaderMesh() {
|
|
204
|
+
const materialRef = useRef()
|
|
205
|
+
|
|
206
|
+
const shaderMaterial = useMemo(() => {
|
|
207
|
+
return new THREE.ShaderMaterial({
|
|
208
|
+
uniforms: {
|
|
209
|
+
time: { value: 0 },
|
|
210
|
+
color: { value: new THREE.Color('cyan') },
|
|
211
|
+
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
|
|
212
|
+
},
|
|
213
|
+
vertexShader: `
|
|
214
|
+
varying vec2 vUv;
|
|
215
|
+
void main() {
|
|
216
|
+
vUv = uv;
|
|
217
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
218
|
+
}
|
|
219
|
+
`,
|
|
220
|
+
fragmentShader: `
|
|
221
|
+
uniform float time;
|
|
222
|
+
uniform vec3 color;
|
|
223
|
+
uniform vec2 resolution;
|
|
224
|
+
varying vec2 vUv;
|
|
225
|
+
|
|
226
|
+
void main() {
|
|
227
|
+
vec2 st = gl_FragCoord.xy / resolution;
|
|
228
|
+
float pattern = sin(st.x * 20.0 + time) * sin(st.y * 20.0 + time);
|
|
229
|
+
gl_FragColor = vec4(color * pattern, 1.0);
|
|
230
|
+
}
|
|
231
|
+
`,
|
|
232
|
+
side: THREE.DoubleSide,
|
|
233
|
+
transparent: true,
|
|
234
|
+
})
|
|
235
|
+
}, [])
|
|
236
|
+
|
|
237
|
+
useFrame(({ clock }) => {
|
|
238
|
+
shaderMaterial.uniforms.time.value = clock.elapsedTime
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<mesh material={shaderMaterial}>
|
|
243
|
+
<planeGeometry args={[4, 4, 32, 32]} />
|
|
244
|
+
</mesh>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Uniforms
|
|
250
|
+
|
|
251
|
+
### Common Uniform Types
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
const MyMaterial = shaderMaterial(
|
|
255
|
+
{
|
|
256
|
+
// Numbers
|
|
257
|
+
time: 0,
|
|
258
|
+
intensity: 1.5,
|
|
259
|
+
|
|
260
|
+
// Vectors
|
|
261
|
+
resolution: new THREE.Vector2(1920, 1080),
|
|
262
|
+
lightPosition: new THREE.Vector3(5, 10, 5),
|
|
263
|
+
bounds: new THREE.Vector4(0, 0, 1, 1),
|
|
264
|
+
|
|
265
|
+
// Color (becomes vec3)
|
|
266
|
+
color: new THREE.Color('#ff0000'),
|
|
267
|
+
|
|
268
|
+
// Matrices
|
|
269
|
+
customMatrix: new THREE.Matrix4(),
|
|
270
|
+
|
|
271
|
+
// Textures
|
|
272
|
+
map: null, // sampler2D
|
|
273
|
+
cubeMap: null, // samplerCube
|
|
274
|
+
|
|
275
|
+
// Arrays
|
|
276
|
+
positions: [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()],
|
|
277
|
+
},
|
|
278
|
+
vertexShader,
|
|
279
|
+
fragmentShader
|
|
280
|
+
)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### GLSL Declarations
|
|
284
|
+
|
|
285
|
+
```glsl
|
|
286
|
+
// In shader code
|
|
287
|
+
uniform float time;
|
|
288
|
+
uniform float intensity;
|
|
289
|
+
uniform vec2 resolution;
|
|
290
|
+
uniform vec3 lightPosition;
|
|
291
|
+
uniform vec3 color; // THREE.Color becomes vec3
|
|
292
|
+
uniform vec4 bounds;
|
|
293
|
+
uniform mat4 customMatrix;
|
|
294
|
+
uniform sampler2D map;
|
|
295
|
+
uniform samplerCube cubeMap;
|
|
296
|
+
uniform vec3 positions[3];
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Updating Uniforms
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
function AnimatedShader() {
|
|
303
|
+
const materialRef = useRef()
|
|
304
|
+
|
|
305
|
+
useFrame(({ clock, mouse, viewport }) => {
|
|
306
|
+
// Direct value update
|
|
307
|
+
materialRef.current.time = clock.elapsedTime
|
|
308
|
+
|
|
309
|
+
// Vector update
|
|
310
|
+
materialRef.current.resolution.set(viewport.width, viewport.height)
|
|
311
|
+
|
|
312
|
+
// Color update
|
|
313
|
+
materialRef.current.color.setHSL((clock.elapsedTime * 0.1) % 1, 1, 0.5)
|
|
314
|
+
|
|
315
|
+
// Or via uniforms object (for THREE.ShaderMaterial)
|
|
316
|
+
// materialRef.current.uniforms.time.value = clock.elapsedTime
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
<mesh>
|
|
321
|
+
<boxGeometry />
|
|
322
|
+
<myShaderMaterial ref={materialRef} />
|
|
323
|
+
</mesh>
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Varyings
|
|
329
|
+
|
|
330
|
+
Pass data from vertex to fragment shader.
|
|
331
|
+
|
|
332
|
+
```glsl
|
|
333
|
+
// Vertex shader
|
|
334
|
+
varying vec2 vUv;
|
|
335
|
+
varying vec3 vNormal;
|
|
336
|
+
varying vec3 vPosition;
|
|
337
|
+
varying vec3 vWorldPosition;
|
|
338
|
+
|
|
339
|
+
void main() {
|
|
340
|
+
vUv = uv;
|
|
341
|
+
vNormal = normalize(normalMatrix * normal);
|
|
342
|
+
vPosition = position;
|
|
343
|
+
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
344
|
+
|
|
345
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Fragment shader
|
|
349
|
+
varying vec2 vUv;
|
|
350
|
+
varying vec3 vNormal;
|
|
351
|
+
varying vec3 vPosition;
|
|
352
|
+
varying vec3 vWorldPosition;
|
|
353
|
+
|
|
354
|
+
void main() {
|
|
355
|
+
// Use interpolated values
|
|
356
|
+
gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0);
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Common Shader Patterns
|
|
361
|
+
|
|
362
|
+
### Texture Sampling
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
import { useTexture } from '@react-three/drei'
|
|
366
|
+
|
|
367
|
+
function TexturedShaderMesh() {
|
|
368
|
+
const texture = useTexture('/textures/color.jpg')
|
|
369
|
+
const materialRef = useRef()
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<mesh>
|
|
373
|
+
<planeGeometry args={[2, 2]} />
|
|
374
|
+
<myShaderMaterial ref={materialRef} map={texture} />
|
|
375
|
+
</mesh>
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Shader
|
|
380
|
+
const TextureMaterial = shaderMaterial(
|
|
381
|
+
{ map: null },
|
|
382
|
+
`
|
|
383
|
+
varying vec2 vUv;
|
|
384
|
+
void main() {
|
|
385
|
+
vUv = uv;
|
|
386
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
387
|
+
}
|
|
388
|
+
`,
|
|
389
|
+
`
|
|
390
|
+
uniform sampler2D map;
|
|
391
|
+
varying vec2 vUv;
|
|
392
|
+
|
|
393
|
+
void main() {
|
|
394
|
+
vec4 texColor = texture2D(map, vUv);
|
|
395
|
+
gl_FragColor = texColor;
|
|
396
|
+
}
|
|
397
|
+
`
|
|
398
|
+
)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Vertex Displacement
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
const WaveMaterial = shaderMaterial(
|
|
405
|
+
{ time: 0, amplitude: 0.5, frequency: 2.0 },
|
|
406
|
+
`
|
|
407
|
+
uniform float time;
|
|
408
|
+
uniform float amplitude;
|
|
409
|
+
uniform float frequency;
|
|
410
|
+
varying vec2 vUv;
|
|
411
|
+
|
|
412
|
+
void main() {
|
|
413
|
+
vUv = uv;
|
|
414
|
+
vec3 pos = position;
|
|
415
|
+
|
|
416
|
+
// Wave displacement
|
|
417
|
+
pos.z += sin(pos.x * frequency + time) * amplitude;
|
|
418
|
+
pos.z += sin(pos.y * frequency + time) * amplitude;
|
|
419
|
+
|
|
420
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
|
|
421
|
+
}
|
|
422
|
+
`,
|
|
423
|
+
`
|
|
424
|
+
varying vec2 vUv;
|
|
425
|
+
void main() {
|
|
426
|
+
gl_FragColor = vec4(vUv, 1.0, 1.0);
|
|
427
|
+
}
|
|
428
|
+
`
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
extend({ WaveMaterial })
|
|
432
|
+
|
|
433
|
+
function WavePlane() {
|
|
434
|
+
const ref = useRef()
|
|
435
|
+
|
|
436
|
+
useFrame(({ clock }) => {
|
|
437
|
+
ref.current.time = clock.elapsedTime
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<mesh rotation={[-Math.PI / 2, 0, 0]}>
|
|
442
|
+
<planeGeometry args={[10, 10, 64, 64]} />
|
|
443
|
+
<waveMaterial ref={ref} />
|
|
444
|
+
</mesh>
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Fresnel Effect
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
const FresnelMaterial = shaderMaterial(
|
|
453
|
+
{ fresnelColor: new THREE.Color('cyan'), baseColor: new THREE.Color('navy') },
|
|
454
|
+
`
|
|
455
|
+
varying vec3 vNormal;
|
|
456
|
+
varying vec3 vWorldPosition;
|
|
457
|
+
|
|
458
|
+
void main() {
|
|
459
|
+
vNormal = normalize(normalMatrix * normal);
|
|
460
|
+
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
461
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
462
|
+
}
|
|
463
|
+
`,
|
|
464
|
+
`
|
|
465
|
+
uniform vec3 fresnelColor;
|
|
466
|
+
uniform vec3 baseColor;
|
|
467
|
+
varying vec3 vNormal;
|
|
468
|
+
varying vec3 vWorldPosition;
|
|
469
|
+
|
|
470
|
+
void main() {
|
|
471
|
+
vec3 viewDirection = normalize(cameraPosition - vWorldPosition);
|
|
472
|
+
float fresnel = pow(1.0 - dot(viewDirection, vNormal), 3.0);
|
|
473
|
+
|
|
474
|
+
gl_FragColor = vec4(mix(baseColor, fresnelColor, fresnel), 1.0);
|
|
475
|
+
}
|
|
476
|
+
`
|
|
477
|
+
)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Noise Functions
|
|
481
|
+
|
|
482
|
+
```glsl
|
|
483
|
+
// Simple random
|
|
484
|
+
float random(vec2 st) {
|
|
485
|
+
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Value noise
|
|
489
|
+
float noise(vec2 st) {
|
|
490
|
+
vec2 i = floor(st);
|
|
491
|
+
vec2 f = fract(st);
|
|
492
|
+
|
|
493
|
+
float a = random(i);
|
|
494
|
+
float b = random(i + vec2(1.0, 0.0));
|
|
495
|
+
float c = random(i + vec2(0.0, 1.0));
|
|
496
|
+
float d = random(i + vec2(1.0, 1.0));
|
|
497
|
+
|
|
498
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
499
|
+
|
|
500
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// FBM (Fractal Brownian Motion)
|
|
504
|
+
float fbm(vec2 st) {
|
|
505
|
+
float value = 0.0;
|
|
506
|
+
float amplitude = 0.5;
|
|
507
|
+
|
|
508
|
+
for (int i = 0; i < 5; i++) {
|
|
509
|
+
value += amplitude * noise(st);
|
|
510
|
+
st *= 2.0;
|
|
511
|
+
amplitude *= 0.5;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return value;
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Gradient
|
|
519
|
+
|
|
520
|
+
```glsl
|
|
521
|
+
// Linear gradient
|
|
522
|
+
vec3 gradient = mix(colorA, colorB, vUv.y);
|
|
523
|
+
|
|
524
|
+
// Radial gradient
|
|
525
|
+
float dist = distance(vUv, vec2(0.5));
|
|
526
|
+
vec3 radial = mix(centerColor, edgeColor, dist * 2.0);
|
|
527
|
+
|
|
528
|
+
// Smooth gradient
|
|
529
|
+
float t = smoothstep(0.0, 1.0, vUv.y);
|
|
530
|
+
vec3 smooth = mix(colorA, colorB, t);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Dissolve Effect
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
const DissolveMaterial = shaderMaterial(
|
|
537
|
+
{ progress: 0, noiseScale: 10.0, edgeColor: new THREE.Color('orange') },
|
|
538
|
+
`
|
|
539
|
+
varying vec2 vUv;
|
|
540
|
+
void main() {
|
|
541
|
+
vUv = uv;
|
|
542
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
543
|
+
}
|
|
544
|
+
`,
|
|
545
|
+
`
|
|
546
|
+
uniform float progress;
|
|
547
|
+
uniform float noiseScale;
|
|
548
|
+
uniform vec3 edgeColor;
|
|
549
|
+
varying vec2 vUv;
|
|
550
|
+
|
|
551
|
+
float random(vec2 st) {
|
|
552
|
+
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
float noise(vec2 st) {
|
|
556
|
+
vec2 i = floor(st);
|
|
557
|
+
vec2 f = fract(st);
|
|
558
|
+
float a = random(i);
|
|
559
|
+
float b = random(i + vec2(1.0, 0.0));
|
|
560
|
+
float c = random(i + vec2(0.0, 1.0));
|
|
561
|
+
float d = random(i + vec2(1.0, 1.0));
|
|
562
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
563
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
void main() {
|
|
567
|
+
float n = noise(vUv * noiseScale);
|
|
568
|
+
|
|
569
|
+
if (n < progress) {
|
|
570
|
+
discard;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
float edge = smoothstep(progress, progress + 0.1, n);
|
|
574
|
+
vec3 baseColor = vec3(0.5);
|
|
575
|
+
|
|
576
|
+
gl_FragColor = vec4(mix(edgeColor, baseColor, edge), 1.0);
|
|
577
|
+
}
|
|
578
|
+
`
|
|
579
|
+
)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Extending Built-in Materials
|
|
583
|
+
|
|
584
|
+
### onBeforeCompile
|
|
585
|
+
|
|
586
|
+
Modify existing material shaders.
|
|
587
|
+
|
|
588
|
+
```tsx
|
|
589
|
+
import { useRef, useEffect } from 'react'
|
|
590
|
+
import { useFrame } from '@react-three/fiber'
|
|
591
|
+
import * as THREE from 'three'
|
|
592
|
+
|
|
593
|
+
function ModifiedStandardMaterial() {
|
|
594
|
+
const materialRef = useRef()
|
|
595
|
+
const shaderRef = useRef()
|
|
596
|
+
|
|
597
|
+
useEffect(() => {
|
|
598
|
+
if (materialRef.current) {
|
|
599
|
+
materialRef.current.onBeforeCompile = (shader) => {
|
|
600
|
+
// Add custom uniform
|
|
601
|
+
shader.uniforms.time = { value: 0 }
|
|
602
|
+
shaderRef.current = shader
|
|
603
|
+
|
|
604
|
+
// Add uniform declaration
|
|
605
|
+
shader.vertexShader = 'uniform float time;\n' + shader.vertexShader
|
|
606
|
+
|
|
607
|
+
// Modify vertex shader
|
|
608
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
609
|
+
'#include <begin_vertex>',
|
|
610
|
+
`
|
|
611
|
+
#include <begin_vertex>
|
|
612
|
+
transformed.y += sin(position.x * 10.0 + time) * 0.1;
|
|
613
|
+
`
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}, [])
|
|
618
|
+
|
|
619
|
+
useFrame(({ clock }) => {
|
|
620
|
+
if (shaderRef.current) {
|
|
621
|
+
shaderRef.current.uniforms.time.value = clock.elapsedTime
|
|
622
|
+
}
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
return (
|
|
626
|
+
<mesh>
|
|
627
|
+
<planeGeometry args={[5, 5, 32, 32]} />
|
|
628
|
+
<meshStandardMaterial ref={materialRef} color="green" />
|
|
629
|
+
</mesh>
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Common Injection Points
|
|
635
|
+
|
|
636
|
+
```javascript
|
|
637
|
+
// Vertex shader chunks
|
|
638
|
+
'#include <begin_vertex>' // After position is calculated
|
|
639
|
+
'#include <project_vertex>' // After gl_Position
|
|
640
|
+
'#include <beginnormal_vertex>' // Normal calculation start
|
|
641
|
+
|
|
642
|
+
// Fragment shader chunks
|
|
643
|
+
'#include <color_fragment>' // After diffuse color
|
|
644
|
+
'#include <output_fragment>' // Final output
|
|
645
|
+
'#include <fog_fragment>' // After fog applied
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## GLSL Built-in Functions
|
|
649
|
+
|
|
650
|
+
### Math Functions
|
|
651
|
+
|
|
652
|
+
```glsl
|
|
653
|
+
// Basic
|
|
654
|
+
abs(x), sign(x), floor(x), ceil(x), fract(x)
|
|
655
|
+
mod(x, y), min(x, y), max(x, y), clamp(x, min, max)
|
|
656
|
+
mix(a, b, t), step(edge, x), smoothstep(edge0, edge1, x)
|
|
657
|
+
|
|
658
|
+
// Trigonometry
|
|
659
|
+
sin(x), cos(x), tan(x)
|
|
660
|
+
asin(x), acos(x), atan(y, x), atan(x)
|
|
661
|
+
|
|
662
|
+
// Exponential
|
|
663
|
+
pow(x, y), exp(x), log(x), sqrt(x)
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Vector Functions
|
|
667
|
+
|
|
668
|
+
```glsl
|
|
669
|
+
length(v), distance(p0, p1), dot(x, y), cross(x, y)
|
|
670
|
+
normalize(v), reflect(I, N), refract(I, N, eta)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Instanced Shaders
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
import { useRef, useMemo } from 'react'
|
|
677
|
+
import { useFrame } from '@react-three/fiber'
|
|
678
|
+
import * as THREE from 'three'
|
|
679
|
+
|
|
680
|
+
function InstancedShaderMesh({ count = 1000 }) {
|
|
681
|
+
const meshRef = useRef()
|
|
682
|
+
|
|
683
|
+
// Create instance attributes
|
|
684
|
+
const { offsets, colors } = useMemo(() => {
|
|
685
|
+
const offsets = new Float32Array(count * 3)
|
|
686
|
+
const colors = new Float32Array(count * 3)
|
|
687
|
+
|
|
688
|
+
for (let i = 0; i < count; i++) {
|
|
689
|
+
offsets[i * 3] = (Math.random() - 0.5) * 20
|
|
690
|
+
offsets[i * 3 + 1] = (Math.random() - 0.5) * 20
|
|
691
|
+
offsets[i * 3 + 2] = (Math.random() - 0.5) * 20
|
|
692
|
+
|
|
693
|
+
colors[i * 3] = Math.random()
|
|
694
|
+
colors[i * 3 + 1] = Math.random()
|
|
695
|
+
colors[i * 3 + 2] = Math.random()
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return { offsets, colors }
|
|
699
|
+
}, [count])
|
|
700
|
+
|
|
701
|
+
const shaderMaterial = useMemo(() => {
|
|
702
|
+
return new THREE.ShaderMaterial({
|
|
703
|
+
uniforms: {
|
|
704
|
+
time: { value: 0 }
|
|
705
|
+
},
|
|
706
|
+
vertexShader: `
|
|
707
|
+
attribute vec3 offset;
|
|
708
|
+
attribute vec3 instanceColor;
|
|
709
|
+
varying vec3 vColor;
|
|
710
|
+
|
|
711
|
+
void main() {
|
|
712
|
+
vColor = instanceColor;
|
|
713
|
+
vec3 pos = position + offset;
|
|
714
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
|
|
715
|
+
}
|
|
716
|
+
`,
|
|
717
|
+
fragmentShader: `
|
|
718
|
+
varying vec3 vColor;
|
|
719
|
+
|
|
720
|
+
void main() {
|
|
721
|
+
gl_FragColor = vec4(vColor, 1.0);
|
|
722
|
+
}
|
|
723
|
+
`
|
|
724
|
+
})
|
|
725
|
+
}, [])
|
|
726
|
+
|
|
727
|
+
useFrame(({ clock }) => {
|
|
728
|
+
shaderMaterial.uniforms.time.value = clock.elapsedTime
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
return (
|
|
732
|
+
<instancedMesh ref={meshRef} args={[null, null, count]} material={shaderMaterial}>
|
|
733
|
+
<boxGeometry args={[0.5, 0.5, 0.5]}>
|
|
734
|
+
<instancedBufferAttribute attach="attributes-offset" args={[offsets, 3]} />
|
|
735
|
+
<instancedBufferAttribute attach="attributes-instanceColor" args={[colors, 3]} />
|
|
736
|
+
</boxGeometry>
|
|
737
|
+
</instancedMesh>
|
|
738
|
+
)
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
## External Shader Files
|
|
743
|
+
|
|
744
|
+
### With Vite/Webpack
|
|
745
|
+
|
|
746
|
+
```tsx
|
|
747
|
+
// shaders/vertex.glsl
|
|
748
|
+
varying vec2 vUv;
|
|
749
|
+
void main() {
|
|
750
|
+
vUv = uv;
|
|
751
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// shaders/fragment.glsl
|
|
755
|
+
uniform float time;
|
|
756
|
+
varying vec2 vUv;
|
|
757
|
+
void main() {
|
|
758
|
+
gl_FragColor = vec4(vUv, sin(time), 1.0);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Component.tsx
|
|
762
|
+
import vertexShader from './shaders/vertex.glsl?raw'
|
|
763
|
+
import fragmentShader from './shaders/fragment.glsl?raw'
|
|
764
|
+
|
|
765
|
+
const MyMaterial = shaderMaterial(
|
|
766
|
+
{ time: 0 },
|
|
767
|
+
vertexShader,
|
|
768
|
+
fragmentShader
|
|
769
|
+
)
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### Vite Config for GLSL
|
|
773
|
+
|
|
774
|
+
```javascript
|
|
775
|
+
// vite.config.js
|
|
776
|
+
import glsl from 'vite-plugin-glsl'
|
|
777
|
+
|
|
778
|
+
export default {
|
|
779
|
+
plugins: [glsl()]
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
## Material Properties
|
|
784
|
+
|
|
785
|
+
```tsx
|
|
786
|
+
<myShaderMaterial
|
|
787
|
+
// Rendering
|
|
788
|
+
transparent={true}
|
|
789
|
+
opacity={1.0}
|
|
790
|
+
side={THREE.DoubleSide}
|
|
791
|
+
depthTest={true}
|
|
792
|
+
depthWrite={true}
|
|
793
|
+
|
|
794
|
+
// Blending
|
|
795
|
+
blending={THREE.NormalBlending}
|
|
796
|
+
// NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending
|
|
797
|
+
|
|
798
|
+
// Wireframe
|
|
799
|
+
wireframe={false}
|
|
800
|
+
|
|
801
|
+
// Custom uniforms
|
|
802
|
+
time={0}
|
|
803
|
+
color="hotpink"
|
|
804
|
+
/>
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
## Debugging Shaders
|
|
808
|
+
|
|
809
|
+
```tsx
|
|
810
|
+
function DebugShaderMesh() {
|
|
811
|
+
const materialRef = useRef()
|
|
812
|
+
|
|
813
|
+
useEffect(() => {
|
|
814
|
+
// Log compiled shaders
|
|
815
|
+
if (materialRef.current) {
|
|
816
|
+
console.log('Vertex:', materialRef.current.vertexShader)
|
|
817
|
+
console.log('Fragment:', materialRef.current.fragmentShader)
|
|
818
|
+
}
|
|
819
|
+
}, [])
|
|
820
|
+
|
|
821
|
+
return (
|
|
822
|
+
<mesh>
|
|
823
|
+
<boxGeometry />
|
|
824
|
+
{/* Debug with visual output */}
|
|
825
|
+
<shaderMaterial
|
|
826
|
+
ref={materialRef}
|
|
827
|
+
fragmentShader={`
|
|
828
|
+
varying vec2 vUv;
|
|
829
|
+
void main() {
|
|
830
|
+
// Debug UV
|
|
831
|
+
gl_FragColor = vec4(vUv, 0.0, 1.0);
|
|
832
|
+
|
|
833
|
+
// Debug normals (in vertex: vNormal = normal)
|
|
834
|
+
// gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0);
|
|
835
|
+
}
|
|
836
|
+
`}
|
|
837
|
+
vertexShader={`
|
|
838
|
+
varying vec2 vUv;
|
|
839
|
+
void main() {
|
|
840
|
+
vUv = uv;
|
|
841
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
842
|
+
}
|
|
843
|
+
`}
|
|
844
|
+
/>
|
|
845
|
+
</mesh>
|
|
846
|
+
)
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
## Performance Tips
|
|
851
|
+
|
|
852
|
+
1. **Minimize uniforms**: Group related values into vectors
|
|
853
|
+
2. **Avoid conditionals**: Use `mix`/`step` instead of `if/else`
|
|
854
|
+
3. **Precalculate in JS**: Move static calculations out of shaders
|
|
855
|
+
4. **Use textures for lookup**: Complex functions as texture lookups
|
|
856
|
+
5. **Limit overdraw**: Avoid unnecessary transparent objects
|
|
857
|
+
|
|
858
|
+
```glsl
|
|
859
|
+
// Instead of:
|
|
860
|
+
if (value > 0.5) {
|
|
861
|
+
color = colorA;
|
|
862
|
+
} else {
|
|
863
|
+
color = colorB;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Use:
|
|
867
|
+
color = mix(colorB, colorA, step(0.5, value));
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
## See Also
|
|
871
|
+
|
|
872
|
+
- `r3f-materials` - Built-in material types
|
|
873
|
+
- `r3f-postprocessing` - Full-screen shader effects
|
|
874
|
+
- `r3f-textures` - Texture sampling in shaders
|