@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.
@@ -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