@lark-apaas/coding-steering 0.1.2 → 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/html/skills/rich-interactive-design/SKILL.md +5 -2
- package/steering/html/skills/rich-interactive-design/references/tasks/interactive-scene.md +1 -0
- 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,635 @@
|
|
|
1
|
+
# React Three Fiber Textures
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Canvas } from '@react-three/fiber'
|
|
7
|
+
import { useTexture } from '@react-three/drei'
|
|
8
|
+
|
|
9
|
+
function TexturedBox() {
|
|
10
|
+
const texture = useTexture('/textures/wood.jpg')
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<mesh>
|
|
14
|
+
<boxGeometry />
|
|
15
|
+
<meshStandardMaterial map={texture} />
|
|
16
|
+
</mesh>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function App() {
|
|
21
|
+
return (
|
|
22
|
+
<Canvas>
|
|
23
|
+
<ambientLight />
|
|
24
|
+
<TexturedBox />
|
|
25
|
+
</Canvas>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## useTexture Hook (Drei)
|
|
31
|
+
|
|
32
|
+
The recommended way to load textures in R3F.
|
|
33
|
+
|
|
34
|
+
### Single Texture
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { useTexture } from '@react-three/drei'
|
|
38
|
+
|
|
39
|
+
function SingleTexture() {
|
|
40
|
+
const texture = useTexture('/textures/color.jpg')
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<mesh>
|
|
44
|
+
<planeGeometry args={[5, 5]} />
|
|
45
|
+
<meshBasicMaterial map={texture} />
|
|
46
|
+
</mesh>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Multiple Textures (Array)
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
function MultipleTextures() {
|
|
55
|
+
const [colorMap, normalMap, roughnessMap] = useTexture([
|
|
56
|
+
'/textures/color.jpg',
|
|
57
|
+
'/textures/normal.jpg',
|
|
58
|
+
'/textures/roughness.jpg',
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<mesh>
|
|
63
|
+
<sphereGeometry args={[1, 64, 64]} />
|
|
64
|
+
<meshStandardMaterial
|
|
65
|
+
map={colorMap}
|
|
66
|
+
normalMap={normalMap}
|
|
67
|
+
roughnessMap={roughnessMap}
|
|
68
|
+
/>
|
|
69
|
+
</mesh>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Named Object (Recommended for PBR)
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
function PBRTextures() {
|
|
78
|
+
// Named object automatically spreads to material
|
|
79
|
+
const textures = useTexture({
|
|
80
|
+
map: '/textures/color.jpg',
|
|
81
|
+
normalMap: '/textures/normal.jpg',
|
|
82
|
+
roughnessMap: '/textures/roughness.jpg',
|
|
83
|
+
metalnessMap: '/textures/metalness.jpg',
|
|
84
|
+
aoMap: '/textures/ao.jpg',
|
|
85
|
+
displacementMap: '/textures/displacement.jpg',
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<mesh>
|
|
90
|
+
<sphereGeometry args={[1, 64, 64]} />
|
|
91
|
+
<meshStandardMaterial
|
|
92
|
+
{...textures}
|
|
93
|
+
displacementScale={0.1}
|
|
94
|
+
/>
|
|
95
|
+
</mesh>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### With Texture Configuration
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { useTexture } from '@react-three/drei'
|
|
104
|
+
import * as THREE from 'three'
|
|
105
|
+
|
|
106
|
+
function ConfiguredTextures() {
|
|
107
|
+
const textures = useTexture({
|
|
108
|
+
map: '/textures/color.jpg',
|
|
109
|
+
normalMap: '/textures/normal.jpg',
|
|
110
|
+
}, (textures) => {
|
|
111
|
+
// Configure textures after loading
|
|
112
|
+
Object.values(textures).forEach(texture => {
|
|
113
|
+
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
|
|
114
|
+
texture.repeat.set(4, 4)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<mesh>
|
|
120
|
+
<planeGeometry args={[10, 10]} />
|
|
121
|
+
<meshStandardMaterial {...textures} />
|
|
122
|
+
</mesh>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Preloading
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { useTexture } from '@react-three/drei'
|
|
131
|
+
|
|
132
|
+
// Preload at module level
|
|
133
|
+
useTexture.preload('/textures/hero.jpg')
|
|
134
|
+
useTexture.preload(['/tex1.jpg', '/tex2.jpg'])
|
|
135
|
+
|
|
136
|
+
function Component() {
|
|
137
|
+
// Will be instant if preloaded
|
|
138
|
+
const texture = useTexture('/textures/hero.jpg')
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## useLoader (Core R3F)
|
|
143
|
+
|
|
144
|
+
For more control over loading.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { useLoader } from '@react-three/fiber'
|
|
148
|
+
import { TextureLoader } from 'three'
|
|
149
|
+
|
|
150
|
+
function WithUseLoader() {
|
|
151
|
+
const texture = useLoader(TextureLoader, '/textures/color.jpg')
|
|
152
|
+
|
|
153
|
+
// Multiple textures
|
|
154
|
+
const [color, normal] = useLoader(TextureLoader, [
|
|
155
|
+
'/textures/color.jpg',
|
|
156
|
+
'/textures/normal.jpg',
|
|
157
|
+
])
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<mesh>
|
|
161
|
+
<boxGeometry />
|
|
162
|
+
<meshStandardMaterial map={color} normalMap={normal} />
|
|
163
|
+
</mesh>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Preload
|
|
168
|
+
useLoader.preload(TextureLoader, '/textures/color.jpg')
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Texture Configuration
|
|
172
|
+
|
|
173
|
+
### Wrapping Modes
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import * as THREE from 'three'
|
|
177
|
+
|
|
178
|
+
function ConfigureWrapping() {
|
|
179
|
+
const texture = useTexture('/textures/tile.jpg', (tex) => {
|
|
180
|
+
// Wrapping
|
|
181
|
+
tex.wrapS = THREE.RepeatWrapping // Horizontal: ClampToEdgeWrapping, RepeatWrapping, MirroredRepeatWrapping
|
|
182
|
+
tex.wrapT = THREE.RepeatWrapping // Vertical
|
|
183
|
+
|
|
184
|
+
// Repeat
|
|
185
|
+
tex.repeat.set(4, 4) // Tile 4x4
|
|
186
|
+
|
|
187
|
+
// Offset
|
|
188
|
+
tex.offset.set(0.5, 0.5) // Shift UV
|
|
189
|
+
|
|
190
|
+
// Rotation
|
|
191
|
+
tex.rotation = Math.PI / 4 // Rotate 45 degrees
|
|
192
|
+
tex.center.set(0.5, 0.5) // Rotation pivot
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<mesh>
|
|
197
|
+
<planeGeometry args={[10, 10]} />
|
|
198
|
+
<meshStandardMaterial map={texture} />
|
|
199
|
+
</mesh>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Filtering
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
function ConfigureFiltering() {
|
|
208
|
+
const texture = useTexture('/textures/color.jpg', (tex) => {
|
|
209
|
+
// Minification (texture larger than screen pixels)
|
|
210
|
+
tex.minFilter = THREE.LinearMipmapLinearFilter // Smooth with mipmaps (default)
|
|
211
|
+
tex.minFilter = THREE.NearestFilter // Pixelated
|
|
212
|
+
tex.minFilter = THREE.LinearFilter // Smooth, no mipmaps
|
|
213
|
+
|
|
214
|
+
// Magnification (texture smaller than screen pixels)
|
|
215
|
+
tex.magFilter = THREE.LinearFilter // Smooth (default)
|
|
216
|
+
tex.magFilter = THREE.NearestFilter // Pixelated (retro style)
|
|
217
|
+
|
|
218
|
+
// Anisotropic filtering (sharper at angles)
|
|
219
|
+
tex.anisotropy = 16 // Usually renderer.capabilities.getMaxAnisotropy()
|
|
220
|
+
|
|
221
|
+
// Generate mipmaps
|
|
222
|
+
tex.generateMipmaps = true // Default
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Color Space
|
|
228
|
+
|
|
229
|
+
Important for accurate colors.
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
function ConfigureColorSpace() {
|
|
233
|
+
const [colorMap, normalMap, roughnessMap] = useTexture([
|
|
234
|
+
'/textures/color.jpg',
|
|
235
|
+
'/textures/normal.jpg',
|
|
236
|
+
'/textures/roughness.jpg',
|
|
237
|
+
], (textures) => {
|
|
238
|
+
// Color/albedo textures should use sRGB
|
|
239
|
+
textures[0].colorSpace = THREE.SRGBColorSpace
|
|
240
|
+
|
|
241
|
+
// Data textures (normal, roughness, metalness, ao) use Linear
|
|
242
|
+
// This is the default, so usually no action needed
|
|
243
|
+
// textures[1].colorSpace = THREE.LinearSRGBColorSpace
|
|
244
|
+
// textures[2].colorSpace = THREE.LinearSRGBColorSpace
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Environment Maps
|
|
250
|
+
|
|
251
|
+
### useEnvironment Hook
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { useEnvironment, Environment } from '@react-three/drei'
|
|
255
|
+
|
|
256
|
+
// Use as texture
|
|
257
|
+
function EnvMappedSphere() {
|
|
258
|
+
const envMap = useEnvironment({ preset: 'sunset' })
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<mesh>
|
|
262
|
+
<sphereGeometry args={[1, 64, 64]} />
|
|
263
|
+
<meshStandardMaterial
|
|
264
|
+
metalness={1}
|
|
265
|
+
roughness={0}
|
|
266
|
+
envMap={envMap}
|
|
267
|
+
/>
|
|
268
|
+
</mesh>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Or use Environment component for scene-wide
|
|
273
|
+
function Scene() {
|
|
274
|
+
return (
|
|
275
|
+
<>
|
|
276
|
+
<Environment preset="sunset" background />
|
|
277
|
+
<Mesh />
|
|
278
|
+
</>
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### HDR Environment
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
import { useEnvironment } from '@react-three/drei'
|
|
287
|
+
|
|
288
|
+
function HDREnvironment() {
|
|
289
|
+
const envMap = useEnvironment({ files: '/hdri/studio.hdr' })
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<mesh>
|
|
293
|
+
<sphereGeometry args={[1, 64, 64]} />
|
|
294
|
+
<meshStandardMaterial
|
|
295
|
+
metalness={1}
|
|
296
|
+
roughness={0}
|
|
297
|
+
envMap={envMap}
|
|
298
|
+
envMapIntensity={1}
|
|
299
|
+
/>
|
|
300
|
+
</mesh>
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Cube Map
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { useCubeTexture } from '@react-three/drei'
|
|
309
|
+
|
|
310
|
+
function CubeMapTexture() {
|
|
311
|
+
const envMap = useCubeTexture(
|
|
312
|
+
['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
|
|
313
|
+
{ path: '/textures/cube/' }
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<mesh>
|
|
318
|
+
<sphereGeometry args={[1, 64, 64]} />
|
|
319
|
+
<meshStandardMaterial envMap={envMap} metalness={1} roughness={0} />
|
|
320
|
+
</mesh>
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Video Textures
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { useVideoTexture } from '@react-three/drei'
|
|
329
|
+
|
|
330
|
+
function VideoPlane() {
|
|
331
|
+
const texture = useVideoTexture('/videos/sample.mp4', {
|
|
332
|
+
start: true,
|
|
333
|
+
loop: true,
|
|
334
|
+
muted: true,
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
return (
|
|
338
|
+
<mesh>
|
|
339
|
+
<planeGeometry args={[16, 9].map(x => x * 0.5)} />
|
|
340
|
+
<meshBasicMaterial map={texture} toneMapped={false} />
|
|
341
|
+
</mesh>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Canvas Textures
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { useRef, useEffect } from 'react'
|
|
350
|
+
import { useFrame } from '@react-three/fiber'
|
|
351
|
+
import * as THREE from 'three'
|
|
352
|
+
|
|
353
|
+
function CanvasTexture() {
|
|
354
|
+
const meshRef = useRef()
|
|
355
|
+
const textureRef = useRef()
|
|
356
|
+
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
const canvas = document.createElement('canvas')
|
|
359
|
+
canvas.width = 256
|
|
360
|
+
canvas.height = 256
|
|
361
|
+
const ctx = canvas.getContext('2d')
|
|
362
|
+
|
|
363
|
+
// Draw on canvas
|
|
364
|
+
ctx.fillStyle = 'red'
|
|
365
|
+
ctx.fillRect(0, 0, 256, 256)
|
|
366
|
+
ctx.fillStyle = 'white'
|
|
367
|
+
ctx.font = '48px Arial'
|
|
368
|
+
ctx.fillText('Hello', 50, 150)
|
|
369
|
+
|
|
370
|
+
textureRef.current = new THREE.CanvasTexture(canvas)
|
|
371
|
+
}, [])
|
|
372
|
+
|
|
373
|
+
// Update texture dynamically
|
|
374
|
+
useFrame(({ clock }) => {
|
|
375
|
+
if (textureRef.current) {
|
|
376
|
+
const canvas = textureRef.current.image
|
|
377
|
+
const ctx = canvas.getContext('2d')
|
|
378
|
+
ctx.fillStyle = `hsl(${clock.elapsedTime * 50}, 100%, 50%)`
|
|
379
|
+
ctx.fillRect(0, 0, 256, 256)
|
|
380
|
+
textureRef.current.needsUpdate = true
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<mesh ref={meshRef}>
|
|
386
|
+
<planeGeometry args={[2, 2]} />
|
|
387
|
+
<meshBasicMaterial map={textureRef.current} />
|
|
388
|
+
</mesh>
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Data Textures
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
import { useMemo } from 'react'
|
|
397
|
+
import * as THREE from 'three'
|
|
398
|
+
|
|
399
|
+
function NoiseTexture() {
|
|
400
|
+
const texture = useMemo(() => {
|
|
401
|
+
const size = 256
|
|
402
|
+
const data = new Uint8Array(size * size * 4)
|
|
403
|
+
|
|
404
|
+
for (let i = 0; i < size * size; i++) {
|
|
405
|
+
const value = Math.random() * 255
|
|
406
|
+
data[i * 4] = value
|
|
407
|
+
data[i * 4 + 1] = value
|
|
408
|
+
data[i * 4 + 2] = value
|
|
409
|
+
data[i * 4 + 3] = 255
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const texture = new THREE.DataTexture(data, size, size)
|
|
413
|
+
texture.needsUpdate = true
|
|
414
|
+
return texture
|
|
415
|
+
}, [])
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<mesh>
|
|
419
|
+
<planeGeometry args={[2, 2]} />
|
|
420
|
+
<meshBasicMaterial map={texture} />
|
|
421
|
+
</mesh>
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Render Targets
|
|
427
|
+
|
|
428
|
+
Render to texture.
|
|
429
|
+
|
|
430
|
+
```tsx
|
|
431
|
+
import { useFBO } from '@react-three/drei'
|
|
432
|
+
import { useFrame } from '@react-three/fiber'
|
|
433
|
+
import { useRef } from 'react'
|
|
434
|
+
|
|
435
|
+
function RenderToTexture() {
|
|
436
|
+
const fbo = useFBO(512, 512)
|
|
437
|
+
const meshRef = useRef()
|
|
438
|
+
const otherSceneRef = useRef()
|
|
439
|
+
|
|
440
|
+
useFrame(({ gl, camera }) => {
|
|
441
|
+
// Render other scene to FBO
|
|
442
|
+
gl.setRenderTarget(fbo)
|
|
443
|
+
gl.render(otherSceneRef.current, camera)
|
|
444
|
+
gl.setRenderTarget(null)
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
return (
|
|
448
|
+
<>
|
|
449
|
+
{/* Scene to render to texture */}
|
|
450
|
+
<group ref={otherSceneRef}>
|
|
451
|
+
<mesh position={[0, 0, -5]}>
|
|
452
|
+
<sphereGeometry args={[1, 32, 32]} />
|
|
453
|
+
<meshStandardMaterial color="red" />
|
|
454
|
+
</mesh>
|
|
455
|
+
</group>
|
|
456
|
+
|
|
457
|
+
{/* Display the texture */}
|
|
458
|
+
<mesh ref={meshRef}>
|
|
459
|
+
<planeGeometry args={[4, 4]} />
|
|
460
|
+
<meshBasicMaterial map={fbo.texture} />
|
|
461
|
+
</mesh>
|
|
462
|
+
</>
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Texture Atlas / Sprite Sheet
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
import { useTexture } from '@react-three/drei'
|
|
471
|
+
import { useState } from 'react'
|
|
472
|
+
import { useFrame } from '@react-three/fiber'
|
|
473
|
+
import * as THREE from 'three'
|
|
474
|
+
|
|
475
|
+
function SpriteAnimation() {
|
|
476
|
+
const texture = useTexture('/textures/spritesheet.png')
|
|
477
|
+
const [frame, setFrame] = useState(0)
|
|
478
|
+
|
|
479
|
+
// Configure texture
|
|
480
|
+
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping
|
|
481
|
+
texture.repeat.set(1/4, 1/4) // 4x4 sprite sheet
|
|
482
|
+
|
|
483
|
+
useFrame(({ clock }) => {
|
|
484
|
+
const newFrame = Math.floor(clock.elapsedTime * 10) % 16
|
|
485
|
+
if (newFrame !== frame) {
|
|
486
|
+
setFrame(newFrame)
|
|
487
|
+
const col = newFrame % 4
|
|
488
|
+
const row = Math.floor(newFrame / 4)
|
|
489
|
+
texture.offset.set(col / 4, 1 - (row + 1) / 4)
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
return (
|
|
494
|
+
<mesh>
|
|
495
|
+
<planeGeometry args={[1, 1]} />
|
|
496
|
+
<meshBasicMaterial map={texture} transparent />
|
|
497
|
+
</mesh>
|
|
498
|
+
)
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## Material Texture Maps Reference
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
<meshStandardMaterial
|
|
506
|
+
// Base color (sRGB)
|
|
507
|
+
map={colorTexture}
|
|
508
|
+
|
|
509
|
+
// Surface detail (Linear)
|
|
510
|
+
normalMap={normalTexture}
|
|
511
|
+
normalScale={[1, 1]}
|
|
512
|
+
|
|
513
|
+
// Roughness (Linear, grayscale)
|
|
514
|
+
roughnessMap={roughnessTexture}
|
|
515
|
+
roughness={1} // Multiplier
|
|
516
|
+
|
|
517
|
+
// Metalness (Linear, grayscale)
|
|
518
|
+
metalnessMap={metalnessTexture}
|
|
519
|
+
metalness={1} // Multiplier
|
|
520
|
+
|
|
521
|
+
// Ambient Occlusion (Linear, requires uv2)
|
|
522
|
+
aoMap={aoTexture}
|
|
523
|
+
aoMapIntensity={1}
|
|
524
|
+
|
|
525
|
+
// Self-illumination (sRGB)
|
|
526
|
+
emissiveMap={emissiveTexture}
|
|
527
|
+
emissive="#ffffff"
|
|
528
|
+
emissiveIntensity={1}
|
|
529
|
+
|
|
530
|
+
// Vertex displacement (Linear)
|
|
531
|
+
displacementMap={displacementTexture}
|
|
532
|
+
displacementScale={0.1}
|
|
533
|
+
displacementBias={0}
|
|
534
|
+
|
|
535
|
+
// Alpha (Linear)
|
|
536
|
+
alphaMap={alphaTexture}
|
|
537
|
+
transparent={true}
|
|
538
|
+
|
|
539
|
+
// Environment reflection
|
|
540
|
+
envMap={envTexture}
|
|
541
|
+
envMapIntensity={1}
|
|
542
|
+
|
|
543
|
+
// Lightmap (requires uv2)
|
|
544
|
+
lightMap={lightmapTexture}
|
|
545
|
+
lightMapIntensity={1}
|
|
546
|
+
/>
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
## Second UV Channel (for AO/Lightmaps)
|
|
550
|
+
|
|
551
|
+
```tsx
|
|
552
|
+
import { useEffect, useRef } from 'react'
|
|
553
|
+
|
|
554
|
+
function MeshWithUV2() {
|
|
555
|
+
const meshRef = useRef()
|
|
556
|
+
|
|
557
|
+
useEffect(() => {
|
|
558
|
+
// Copy uv to uv2 for aoMap/lightMap
|
|
559
|
+
const geometry = meshRef.current.geometry
|
|
560
|
+
geometry.setAttribute('uv2', geometry.attributes.uv)
|
|
561
|
+
}, [])
|
|
562
|
+
|
|
563
|
+
return (
|
|
564
|
+
<mesh ref={meshRef}>
|
|
565
|
+
<boxGeometry />
|
|
566
|
+
<meshStandardMaterial
|
|
567
|
+
aoMap={aoTexture}
|
|
568
|
+
aoMapIntensity={1}
|
|
569
|
+
/>
|
|
570
|
+
</mesh>
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Suspense Loading
|
|
576
|
+
|
|
577
|
+
```tsx
|
|
578
|
+
import { Suspense } from 'react'
|
|
579
|
+
import { useTexture } from '@react-three/drei'
|
|
580
|
+
|
|
581
|
+
function TexturedMesh() {
|
|
582
|
+
const texture = useTexture('/textures/large.jpg')
|
|
583
|
+
return (
|
|
584
|
+
<mesh>
|
|
585
|
+
<boxGeometry />
|
|
586
|
+
<meshStandardMaterial map={texture} />
|
|
587
|
+
</mesh>
|
|
588
|
+
)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function Fallback() {
|
|
592
|
+
return (
|
|
593
|
+
<mesh>
|
|
594
|
+
<boxGeometry />
|
|
595
|
+
<meshBasicMaterial color="gray" wireframe />
|
|
596
|
+
</mesh>
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function Scene() {
|
|
601
|
+
return (
|
|
602
|
+
<Suspense fallback={<Fallback />}>
|
|
603
|
+
<TexturedMesh />
|
|
604
|
+
</Suspense>
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Performance Tips
|
|
610
|
+
|
|
611
|
+
1. **Use power-of-2 dimensions**: 256, 512, 1024, 2048
|
|
612
|
+
2. **Compress textures**: Use KTX2/Basis for web
|
|
613
|
+
3. **Enable mipmaps**: For distant objects
|
|
614
|
+
4. **Limit texture size**: 2048 usually sufficient
|
|
615
|
+
5. **Reuse textures**: Same texture = better batching
|
|
616
|
+
6. **Preload important textures**: Avoid pop-in
|
|
617
|
+
|
|
618
|
+
```tsx
|
|
619
|
+
// Preload critical textures
|
|
620
|
+
useTexture.preload('/textures/hero.jpg')
|
|
621
|
+
|
|
622
|
+
// Check texture memory
|
|
623
|
+
useFrame(({ gl }) => {
|
|
624
|
+
console.log('Textures:', gl.info.memory.textures)
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
// Dispose unused textures (R3F usually handles this)
|
|
628
|
+
texture.dispose()
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
## See Also
|
|
632
|
+
|
|
633
|
+
- `r3f-materials` - Applying textures to materials
|
|
634
|
+
- `r3f-loaders` - Asset loading patterns
|
|
635
|
+
- `r3f-shaders` - Custom texture sampling
|