omgkit 2.2.0 → 2.3.1

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.
Files changed (60) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/plugin/skills/databases/database-management/SKILL.md +288 -0
  4. package/plugin/skills/databases/database-migration/SKILL.md +285 -0
  5. package/plugin/skills/databases/database-schema-design/SKILL.md +195 -0
  6. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  7. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  8. package/plugin/skills/databases/redis/SKILL.md +53 -860
  9. package/plugin/skills/databases/supabase/SKILL.md +283 -0
  10. package/plugin/skills/devops/aws/SKILL.md +68 -672
  11. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  12. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  13. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  14. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  15. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  16. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  17. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  18. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  19. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  20. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  21. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  23. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  26. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  27. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  28. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  29. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  30. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  31. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  32. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  33. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  34. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  35. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  36. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  37. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  38. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  39. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  40. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  41. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  42. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  43. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  44. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  45. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  46. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  47. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  48. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  49. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  50. package/plugin/skills/security/oauth/SKILL.md +80 -934
  51. package/plugin/skills/security/owasp/SKILL.md +78 -862
  52. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  53. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  54. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  55. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  56. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  57. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  58. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  59. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  60. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,265 +1,57 @@
1
1
  ---
2
- name: threejs
3
- description: Three.js 3D graphics with WebGL, React Three Fiber, shaders, physics, and immersive experiences
4
- category: frontend
5
- triggers:
6
- - three.js
7
- - threejs
8
- - 3d graphics
9
- - webgl
10
- - react three fiber
11
- - r3f
12
- - 3d animation
13
- - shaders
14
- - 3d scenes
2
+ name: building-3d-graphics
3
+ description: Claude builds immersive 3D web experiences with Three.js and React Three Fiber. Use when creating WebGL scenes, 3D animations, shaders, or physics simulations.
15
4
  ---
16
5
 
17
- # Three.js 3D Graphics
6
+ # Building 3D Graphics
18
7
 
19
- Enterprise-grade **Three.js 3D graphics** following industry best practices. This skill covers scene setup, React Three Fiber integration, materials and lighting, animations, shaders, physics simulation, and performance optimization used by top engineering teams.
8
+ ## Quick Start
20
9
 
21
- ## Purpose
22
-
23
- Build immersive 3D web experiences:
24
-
25
- - Create interactive 3D scenes with proper camera controls
26
- - Build declarative 3D with React Three Fiber
27
- - Implement physically-based rendering (PBR) materials
28
- - Add realistic lighting and shadows
29
- - Create smooth animations and transitions
30
- - Write custom shaders for visual effects
31
- - Integrate physics simulations
32
- - Optimize performance for production
33
-
34
- ## Features
35
-
36
- ### 1. Core Three.js Setup
37
-
38
- ```typescript
39
- // lib/three/SceneManager.ts
40
- import * as THREE from 'three';
41
- import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
42
- import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
43
- import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
44
-
45
- interface SceneManagerOptions {
46
- container: HTMLElement;
47
- width?: number;
48
- height?: number;
49
- antialias?: boolean;
50
- alpha?: boolean;
51
- pixelRatio?: number;
52
- }
53
-
54
- export class SceneManager {
55
- private scene: THREE.Scene;
56
- private camera: THREE.PerspectiveCamera;
57
- private renderer: THREE.WebGLRenderer;
58
- private controls: OrbitControls;
59
- private composer: EffectComposer;
60
- private clock: THREE.Clock;
61
- private animationFrameId: number | null = null;
62
- private updateCallbacks: Set<(delta: number, elapsed: number) => void> = new Set();
63
-
64
- constructor(options: SceneManagerOptions) {
65
- const {
66
- container,
67
- width = container.clientWidth,
68
- height = container.clientHeight,
69
- antialias = true,
70
- alpha = false,
71
- pixelRatio = Math.min(window.devicePixelRatio, 2),
72
- } = options;
73
-
74
- // Scene
75
- this.scene = new THREE.Scene();
76
- this.scene.background = alpha ? null : new THREE.Color(0x111111);
77
-
78
- // Camera
79
- this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
80
- this.camera.position.set(0, 2, 5);
81
-
82
- // Renderer
83
- this.renderer = new THREE.WebGLRenderer({
84
- antialias,
85
- alpha,
86
- powerPreference: 'high-performance',
87
- });
88
- this.renderer.setSize(width, height);
89
- this.renderer.setPixelRatio(pixelRatio);
90
- this.renderer.shadowMap.enabled = true;
91
- this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
92
- this.renderer.outputColorSpace = THREE.SRGBColorSpace;
93
- this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
94
- this.renderer.toneMappingExposure = 1.0;
95
- container.appendChild(this.renderer.domElement);
96
-
97
- // Controls
98
- this.controls = new OrbitControls(this.camera, this.renderer.domElement);
99
- this.controls.enableDamping = true;
100
- this.controls.dampingFactor = 0.05;
101
- this.controls.minDistance = 1;
102
- this.controls.maxDistance = 100;
103
-
104
- // Post-processing
105
- this.composer = new EffectComposer(this.renderer);
106
- this.composer.addPass(new RenderPass(this.scene, this.camera));
107
-
108
- // Clock
109
- this.clock = new THREE.Clock();
110
-
111
- // Handle resize
112
- this.handleResize = this.handleResize.bind(this);
113
- window.addEventListener('resize', this.handleResize);
114
- }
115
-
116
- private handleResize(): void {
117
- const container = this.renderer.domElement.parentElement;
118
- if (!container) return;
119
-
120
- const width = container.clientWidth;
121
- const height = container.clientHeight;
122
-
123
- this.camera.aspect = width / height;
124
- this.camera.updateProjectionMatrix();
125
-
126
- this.renderer.setSize(width, height);
127
- this.composer.setSize(width, height);
128
- }
129
-
130
- public addUpdateCallback(callback: (delta: number, elapsed: number) => void): void {
131
- this.updateCallbacks.add(callback);
132
- }
133
-
134
- public removeUpdateCallback(callback: (delta: number, elapsed: number) => void): void {
135
- this.updateCallbacks.delete(callback);
136
- }
137
-
138
- public start(): void {
139
- if (this.animationFrameId !== null) return;
140
-
141
- const animate = () => {
142
- this.animationFrameId = requestAnimationFrame(animate);
143
-
144
- const delta = this.clock.getDelta();
145
- const elapsed = this.clock.getElapsedTime();
146
-
147
- // Update controls
148
- this.controls.update();
149
-
150
- // Run update callbacks
151
- this.updateCallbacks.forEach((callback) => callback(delta, elapsed));
152
-
153
- // Render
154
- this.composer.render();
155
- };
156
-
157
- animate();
158
- }
159
-
160
- public stop(): void {
161
- if (this.animationFrameId !== null) {
162
- cancelAnimationFrame(this.animationFrameId);
163
- this.animationFrameId = null;
164
- }
165
- }
166
-
167
- public getScene(): THREE.Scene {
168
- return this.scene;
169
- }
170
-
171
- public getCamera(): THREE.PerspectiveCamera {
172
- return this.camera;
173
- }
174
-
175
- public getRenderer(): THREE.WebGLRenderer {
176
- return this.renderer;
177
- }
178
-
179
- public dispose(): void {
180
- this.stop();
181
- window.removeEventListener('resize', this.handleResize);
182
-
183
- // Dispose of all objects in scene
184
- this.scene.traverse((object) => {
185
- if (object instanceof THREE.Mesh) {
186
- object.geometry.dispose();
187
- if (Array.isArray(object.material)) {
188
- object.material.forEach((m) => m.dispose());
189
- } else {
190
- object.material.dispose();
191
- }
192
- }
193
- });
10
+ ```tsx
11
+ import { Canvas } from '@react-three/fiber';
12
+ import { OrbitControls, Environment } from '@react-three/drei';
194
13
 
195
- this.renderer.dispose();
196
- this.controls.dispose();
197
- }
14
+ export function Scene() {
15
+ return (
16
+ <Canvas shadows camera={{ position: [5, 3, 5], fov: 50 }}>
17
+ <ambientLight intensity={0.5} />
18
+ <directionalLight position={[10, 10, 5]} castShadow />
19
+ <mesh castShadow>
20
+ <boxGeometry args={[1, 1, 1]} />
21
+ <meshStandardMaterial color="#4ecdc4" />
22
+ </mesh>
23
+ <OrbitControls enableDamping />
24
+ <Environment preset="city" />
25
+ </Canvas>
26
+ );
198
27
  }
28
+ ```
199
29
 
200
- // Usage
201
- const container = document.getElementById('canvas-container')!;
202
- const sceneManager = new SceneManager({ container, antialias: true });
203
-
204
- // Add objects
205
- const geometry = new THREE.BoxGeometry(1, 1, 1);
206
- const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
207
- const cube = new THREE.Mesh(geometry, material);
208
- cube.castShadow = true;
209
- sceneManager.getScene().add(cube);
30
+ ## Features
210
31
 
211
- // Add lights
212
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
213
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
214
- directionalLight.position.set(5, 10, 5);
215
- directionalLight.castShadow = true;
216
- sceneManager.getScene().add(ambientLight, directionalLight);
32
+ | Feature | Description | Guide |
33
+ |---------|-------------|-------|
34
+ | Scene Management | Renderer, camera, controls setup with proper disposal | `ref/scene-manager.md` |
35
+ | React Three Fiber | Declarative 3D with React components and hooks | `ref/r3f-patterns.md` |
36
+ | Custom Shaders | GLSL vertex/fragment shaders with uniforms | `ref/shader-materials.md` |
37
+ | Physics | Rapier physics with rigid bodies and colliders | `ref/physics-system.md` |
38
+ | Animation | GSAP and Three.js animation mixer integration | `ref/animation.md` |
39
+ | Performance | LOD, instancing, frustum culling, texture optimization | `ref/optimization.md` |
217
40
 
218
- // Animation
219
- sceneManager.addUpdateCallback((delta) => {
220
- cube.rotation.x += delta;
221
- cube.rotation.y += delta * 0.5;
222
- });
41
+ ## Common Patterns
223
42
 
224
- sceneManager.start();
225
- ```
226
-
227
- ### 2. React Three Fiber Integration
43
+ ### Animated Component with Interaction
228
44
 
229
45
  ```tsx
230
- // components/Scene.tsx
231
- import { Canvas, useFrame, useThree } from '@react-three/fiber';
232
- import {
233
- OrbitControls,
234
- Environment,
235
- ContactShadows,
236
- PerspectiveCamera,
237
- useGLTF,
238
- Float,
239
- Text3D,
240
- Center,
241
- MeshTransmissionMaterial,
242
- } from '@react-three/drei';
243
- import { Suspense, useRef, useState } from 'react';
244
- import * as THREE from 'three';
245
-
246
- // Animated box component
247
46
  function AnimatedBox({ position }: { position: [number, number, number] }) {
248
47
  const meshRef = useRef<THREE.Mesh>(null);
249
48
  const [hovered, setHovered] = useState(false);
250
- const [clicked, setClicked] = useState(false);
251
49
 
252
50
  useFrame((state, delta) => {
253
51
  if (meshRef.current) {
254
- meshRef.current.rotation.x += delta * 0.5;
255
- meshRef.current.rotation.y += delta * 0.3;
256
-
257
- // Smooth scale animation
258
- const targetScale = clicked ? 1.5 : hovered ? 1.2 : 1;
259
- meshRef.current.scale.lerp(
260
- new THREE.Vector3(targetScale, targetScale, targetScale),
261
- 0.1
262
- );
52
+ meshRef.current.rotation.y += delta * 0.5;
53
+ const scale = hovered ? 1.2 : 1;
54
+ meshRef.current.scale.lerp(new THREE.Vector3(scale, scale, scale), 0.1);
263
55
  }
264
56
  });
265
57
 
@@ -269,1060 +61,74 @@ function AnimatedBox({ position }: { position: [number, number, number] }) {
269
61
  position={position}
270
62
  onPointerOver={() => setHovered(true)}
271
63
  onPointerOut={() => setHovered(false)}
272
- onClick={() => setClicked(!clicked)}
273
64
  >
274
65
  <boxGeometry args={[1, 1, 1]} />
275
- <meshStandardMaterial
276
- color={hovered ? '#ff6b6b' : '#4ecdc4'}
277
- metalness={0.5}
278
- roughness={0.2}
279
- />
280
- </mesh>
281
- );
282
- }
283
-
284
- // GLTF Model loader
285
- function Model({ url, scale = 1 }: { url: string; scale?: number }) {
286
- const { scene } = useGLTF(url);
287
- const modelRef = useRef<THREE.Group>(null);
288
-
289
- // Clone materials for unique instances
290
- useEffect(() => {
291
- scene.traverse((child) => {
292
- if (child instanceof THREE.Mesh) {
293
- child.castShadow = true;
294
- child.receiveShadow = true;
295
- }
296
- });
297
- }, [scene]);
298
-
299
- return (
300
- <primitive
301
- ref={modelRef}
302
- object={scene}
303
- scale={scale}
304
- dispose={null}
305
- />
306
- );
307
- }
308
-
309
- // Glass material sphere
310
- function GlassSphere() {
311
- return (
312
- <mesh position={[2, 1, 0]}>
313
- <sphereGeometry args={[0.8, 64, 64]} />
314
- <MeshTransmissionMaterial
315
- backside
316
- samples={16}
317
- thickness={0.5}
318
- chromaticAberration={0.5}
319
- anisotropy={0.3}
320
- distortion={0.2}
321
- distortionScale={0.5}
322
- temporalDistortion={0.1}
323
- iridescence={1}
324
- iridescenceIOR={1}
325
- iridescenceThicknessRange={[0, 1400]}
326
- />
66
+ <meshStandardMaterial color={hovered ? '#ff6b6b' : '#4ecdc4'} />
327
67
  </mesh>
328
68
  );
329
69
  }
330
-
331
- // 3D Text
332
- function Text3DComponent({ text }: { text: string }) {
333
- return (
334
- <Center position={[0, 2, 0]}>
335
- <Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
336
- <Text3D
337
- font="/fonts/Inter_Bold.json"
338
- size={0.5}
339
- height={0.1}
340
- curveSegments={12}
341
- >
342
- {text}
343
- <meshStandardMaterial color="#ff6b6b" metalness={0.8} roughness={0.2} />
344
- </Text3D>
345
- </Float>
346
- </Center>
347
- );
348
- }
349
-
350
- // Camera rig with smooth following
351
- function CameraRig({ target }: { target: THREE.Vector3 }) {
352
- const { camera } = useThree();
353
-
354
- useFrame(() => {
355
- camera.position.lerp(
356
- new THREE.Vector3(target.x + 5, target.y + 3, target.z + 5),
357
- 0.02
358
- );
359
- camera.lookAt(target);
360
- });
361
-
362
- return null;
363
- }
364
-
365
- // Main scene
366
- export function Scene() {
367
- return (
368
- <Canvas
369
- shadows
370
- dpr={[1, 2]}
371
- gl={{
372
- antialias: true,
373
- toneMapping: THREE.ACESFilmicToneMapping,
374
- toneMappingExposure: 1,
375
- }}
376
- >
377
- <PerspectiveCamera makeDefault position={[5, 3, 5]} fov={50} />
378
- <OrbitControls
379
- enableDamping
380
- dampingFactor={0.05}
381
- minDistance={2}
382
- maxDistance={20}
383
- />
384
-
385
- {/* Lighting */}
386
- <ambientLight intensity={0.5} />
387
- <directionalLight
388
- position={[10, 10, 5]}
389
- intensity={1.5}
390
- castShadow
391
- shadow-mapSize={[2048, 2048]}
392
- shadow-camera-far={50}
393
- shadow-camera-left={-10}
394
- shadow-camera-right={10}
395
- shadow-camera-top={10}
396
- shadow-camera-bottom={-10}
397
- />
398
-
399
- {/* Environment */}
400
- <Environment preset="city" />
401
-
402
- {/* Content */}
403
- <Suspense fallback={null}>
404
- <AnimatedBox position={[-2, 0.5, 0]} />
405
- <AnimatedBox position={[0, 0.5, 0]} />
406
- <GlassSphere />
407
- <Text3DComponent text="Hello 3D" />
408
-
409
- {/* Ground */}
410
- <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.5, 0]} receiveShadow>
411
- <planeGeometry args={[50, 50]} />
412
- <meshStandardMaterial color="#1a1a2e" />
413
- </mesh>
414
- </Suspense>
415
-
416
- {/* Contact shadows */}
417
- <ContactShadows
418
- position={[0, -0.49, 0]}
419
- opacity={0.5}
420
- scale={20}
421
- blur={2}
422
- far={10}
423
- />
424
- </Canvas>
425
- );
426
- }
427
- ```
428
-
429
- ### 3. Custom Shaders
430
-
431
- ```tsx
432
- // shaders/GradientMaterial.tsx
433
- import { shaderMaterial } from '@react-three/drei';
434
- import { extend, useFrame } from '@react-three/fiber';
435
- import { useRef } from 'react';
436
- import * as THREE from 'three';
437
-
438
- // Vertex shader
439
- const vertexShader = `
440
- varying vec2 vUv;
441
- varying vec3 vPosition;
442
- varying vec3 vNormal;
443
-
444
- uniform float uTime;
445
- uniform float uAmplitude;
446
- uniform float uFrequency;
447
-
448
- void main() {
449
- vUv = uv;
450
- vPosition = position;
451
- vNormal = normal;
452
-
453
- // Wave displacement
454
- vec3 pos = position;
455
- float displacement = sin(pos.x * uFrequency + uTime) *
456
- sin(pos.z * uFrequency + uTime) *
457
- uAmplitude;
458
- pos.y += displacement;
459
-
460
- gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
461
- }
462
- `;
463
-
464
- // Fragment shader
465
- const fragmentShader = `
466
- varying vec2 vUv;
467
- varying vec3 vPosition;
468
- varying vec3 vNormal;
469
-
470
- uniform float uTime;
471
- uniform vec3 uColorA;
472
- uniform vec3 uColorB;
473
- uniform float uNoiseScale;
474
-
475
- // Simplex noise function
476
- vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
477
- vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
478
- vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
479
- vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
480
-
481
- float snoise(vec3 v) {
482
- const vec2 C = vec2(1.0/6.0, 1.0/3.0);
483
- const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
484
-
485
- vec3 i = floor(v + dot(v, C.yyy));
486
- vec3 x0 = v - i + dot(i, C.xxx);
487
-
488
- vec3 g = step(x0.yzx, x0.xyz);
489
- vec3 l = 1.0 - g;
490
- vec3 i1 = min(g.xyz, l.zxy);
491
- vec3 i2 = max(g.xyz, l.zxy);
492
-
493
- vec3 x1 = x0 - i1 + C.xxx;
494
- vec3 x2 = x0 - i2 + C.yyy;
495
- vec3 x3 = x0 - D.yyy;
496
-
497
- i = mod289(i);
498
- vec4 p = permute(permute(permute(
499
- i.z + vec4(0.0, i1.z, i2.z, 1.0))
500
- + i.y + vec4(0.0, i1.y, i2.y, 1.0))
501
- + i.x + vec4(0.0, i1.x, i2.x, 1.0));
502
-
503
- float n_ = 0.142857142857;
504
- vec3 ns = n_ * D.wyz - D.xzx;
505
-
506
- vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
507
-
508
- vec4 x_ = floor(j * ns.z);
509
- vec4 y_ = floor(j - 7.0 * x_);
510
-
511
- vec4 x = x_ *ns.x + ns.yyyy;
512
- vec4 y = y_ *ns.x + ns.yyyy;
513
- vec4 h = 1.0 - abs(x) - abs(y);
514
-
515
- vec4 b0 = vec4(x.xy, y.xy);
516
- vec4 b1 = vec4(x.zw, y.zw);
517
-
518
- vec4 s0 = floor(b0)*2.0 + 1.0;
519
- vec4 s1 = floor(b1)*2.0 + 1.0;
520
- vec4 sh = -step(h, vec4(0.0));
521
-
522
- vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
523
- vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
524
-
525
- vec3 p0 = vec3(a0.xy,h.x);
526
- vec3 p1 = vec3(a0.zw,h.y);
527
- vec3 p2 = vec3(a1.xy,h.z);
528
- vec3 p3 = vec3(a1.zw,h.w);
529
-
530
- vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
531
- p0 *= norm.x;
532
- p1 *= norm.y;
533
- p2 *= norm.z;
534
- p3 *= norm.w;
535
-
536
- vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
537
- m = m * m;
538
- return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
539
- }
540
-
541
- void main() {
542
- // Animated noise
543
- float noise = snoise(vec3(vPosition.xz * uNoiseScale, uTime * 0.3));
544
- noise = noise * 0.5 + 0.5; // Normalize to 0-1
545
-
546
- // Gradient based on UV and noise
547
- float gradient = vUv.y + noise * 0.3;
548
-
549
- // Mix colors
550
- vec3 color = mix(uColorA, uColorB, gradient);
551
-
552
- // Add rim lighting
553
- vec3 viewDirection = normalize(cameraPosition - vPosition);
554
- float rimLight = 1.0 - max(0.0, dot(viewDirection, vNormal));
555
- rimLight = pow(rimLight, 3.0);
556
- color += rimLight * 0.3;
557
-
558
- gl_FragColor = vec4(color, 1.0);
559
- }
560
- `;
561
-
562
- // Create shader material
563
- const GradientMaterial = shaderMaterial(
564
- {
565
- uTime: 0,
566
- uColorA: new THREE.Color('#ff6b6b'),
567
- uColorB: new THREE.Color('#4ecdc4'),
568
- uAmplitude: 0.2,
569
- uFrequency: 2.0,
570
- uNoiseScale: 1.0,
571
- },
572
- vertexShader,
573
- fragmentShader
574
- );
575
-
576
- extend({ GradientMaterial });
577
-
578
- // TypeScript declaration
579
- declare global {
580
- namespace JSX {
581
- interface IntrinsicElements {
582
- gradientMaterial: any;
583
- }
584
- }
585
- }
586
-
587
- // Component using custom shader
588
- export function ShaderPlane() {
589
- const materialRef = useRef<THREE.ShaderMaterial>(null);
590
-
591
- useFrame(({ clock }) => {
592
- if (materialRef.current) {
593
- materialRef.current.uniforms.uTime.value = clock.getElapsedTime();
594
- }
595
- });
596
-
597
- return (
598
- <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
599
- <planeGeometry args={[10, 10, 64, 64]} />
600
- <gradientMaterial
601
- ref={materialRef}
602
- uColorA="#ff6b6b"
603
- uColorB="#4ecdc4"
604
- uAmplitude={0.3}
605
- uFrequency={3.0}
606
- />
607
- </mesh>
608
- );
609
- }
610
-
611
- // Post-processing shader
612
- const GlitchShader = {
613
- uniforms: {
614
- tDiffuse: { value: null },
615
- uTime: { value: 0 },
616
- uIntensity: { value: 0.5 },
617
- },
618
- vertexShader: `
619
- varying vec2 vUv;
620
- void main() {
621
- vUv = uv;
622
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
623
- }
624
- `,
625
- fragmentShader: `
626
- uniform sampler2D tDiffuse;
627
- uniform float uTime;
628
- uniform float uIntensity;
629
- varying vec2 vUv;
630
-
631
- float random(vec2 st) {
632
- return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
633
- }
634
-
635
- void main() {
636
- vec2 uv = vUv;
637
-
638
- // Random glitch offset
639
- float glitch = step(0.99, random(vec2(uTime * 0.1, floor(uv.y * 20.0))));
640
- uv.x += glitch * uIntensity * (random(vec2(uTime)) - 0.5);
641
-
642
- // RGB split
643
- float r = texture2D(tDiffuse, uv + vec2(uIntensity * 0.01, 0.0)).r;
644
- float g = texture2D(tDiffuse, uv).g;
645
- float b = texture2D(tDiffuse, uv - vec2(uIntensity * 0.01, 0.0)).b;
646
-
647
- gl_FragColor = vec4(r, g, b, 1.0);
648
- }
649
- `,
650
- };
651
70
  ```
652
71
 
653
- ### 4. Physics Integration
72
+ ### Instanced Mesh for Performance
654
73
 
655
74
  ```tsx
656
- // components/PhysicsScene.tsx
657
- import { Canvas } from '@react-three/fiber';
658
- import { Physics, RigidBody, CuboidCollider, BallCollider } from '@react-three/rapier';
659
- import { useRef, useState } from 'react';
660
- import * as THREE from 'three';
661
-
662
- // Physics-enabled box
663
- function PhysicsBox({ position }: { position: [number, number, number] }) {
664
- const [color, setColor] = useState('#ff6b6b');
665
-
666
- return (
667
- <RigidBody
668
- position={position}
669
- restitution={0.7}
670
- friction={0.5}
671
- onCollisionEnter={() => setColor('#4ecdc4')}
672
- onCollisionExit={() => setColor('#ff6b6b')}
673
- >
674
- <mesh castShadow>
675
- <boxGeometry args={[1, 1, 1]} />
676
- <meshStandardMaterial color={color} />
677
- </mesh>
678
- </RigidBody>
679
- );
680
- }
681
-
682
- // Bouncing ball
683
- function BouncingBall({ position }: { position: [number, number, number] }) {
684
- const rigidBodyRef = useRef(null);
685
-
686
- const handleClick = () => {
687
- if (rigidBodyRef.current) {
688
- // Apply upward impulse on click
689
- rigidBodyRef.current.applyImpulse({ x: 0, y: 10, z: 0 }, true);
690
- }
691
- };
692
-
693
- return (
694
- <RigidBody
695
- ref={rigidBodyRef}
696
- position={position}
697
- restitution={0.9}
698
- friction={0.3}
699
- colliders="ball"
700
- >
701
- <mesh castShadow onClick={handleClick}>
702
- <sphereGeometry args={[0.5, 32, 32]} />
703
- <meshStandardMaterial color="#feca57" metalness={0.3} roughness={0.2} />
704
- </mesh>
705
- </RigidBody>
706
- );
707
- }
708
-
709
- // Ground plane
710
- function Ground() {
711
- return (
712
- <RigidBody type="fixed" friction={1}>
713
- <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.5, 0]} receiveShadow>
714
- <planeGeometry args={[50, 50]} />
715
- <meshStandardMaterial color="#1a1a2e" />
716
- </mesh>
717
- </RigidBody>
718
- );
719
- }
720
-
721
- // Walls for containment
722
- function Walls() {
723
- return (
724
- <>
725
- {/* Back wall */}
726
- <RigidBody type="fixed">
727
- <CuboidCollider args={[25, 10, 0.5]} position={[0, 5, -25]} />
728
- </RigidBody>
729
- {/* Front wall */}
730
- <RigidBody type="fixed">
731
- <CuboidCollider args={[25, 10, 0.5]} position={[0, 5, 25]} />
732
- </RigidBody>
733
- {/* Left wall */}
734
- <RigidBody type="fixed">
735
- <CuboidCollider args={[0.5, 10, 25]} position={[-25, 5, 0]} />
736
- </RigidBody>
737
- {/* Right wall */}
738
- <RigidBody type="fixed">
739
- <CuboidCollider args={[0.5, 10, 25]} position={[25, 5, 0]} />
740
- </RigidBody>
741
- </>
742
- );
743
- }
744
-
745
- // Spawner for dynamic objects
746
- function ObjectSpawner() {
747
- const [objects, setObjects] = useState<Array<{ id: number; type: 'box' | 'ball'; position: [number, number, number] }>>([]);
748
-
749
- const spawnObject = () => {
750
- const id = Date.now();
751
- const type = Math.random() > 0.5 ? 'box' : 'ball';
752
- const position: [number, number, number] = [
753
- (Math.random() - 0.5) * 10,
754
- 10,
755
- (Math.random() - 0.5) * 10,
756
- ];
757
- setObjects((prev) => [...prev, { id, type, position }]);
758
- };
759
-
760
- return (
761
- <>
762
- {objects.map(({ id, type, position }) =>
763
- type === 'box' ? (
764
- <PhysicsBox key={id} position={position} />
765
- ) : (
766
- <BouncingBall key={id} position={position} />
767
- )
768
- )}
769
-
770
- {/* Spawn trigger - invisible clickable plane */}
771
- <mesh position={[0, 15, 0]} onClick={spawnObject}>
772
- <planeGeometry args={[20, 20]} />
773
- <meshBasicMaterial visible={false} />
774
- </mesh>
775
- </>
776
- );
777
- }
778
-
779
- // Main physics scene
780
- export function PhysicsScene() {
781
- return (
782
- <Canvas shadows camera={{ position: [15, 15, 15], fov: 50 }}>
783
- <ambientLight intensity={0.5} />
784
- <directionalLight
785
- position={[10, 20, 10]}
786
- intensity={1.5}
787
- castShadow
788
- shadow-mapSize={[2048, 2048]}
789
- />
790
-
791
- <Physics gravity={[0, -9.81, 0]} debug={false}>
792
- <Ground />
793
- <Walls />
794
- <ObjectSpawner />
795
-
796
- {/* Initial objects */}
797
- <PhysicsBox position={[0, 5, 0]} />
798
- <PhysicsBox position={[0.5, 7, 0.5]} />
799
- <BouncingBall position={[-2, 5, 0]} />
800
- <BouncingBall position={[2, 8, 2]} />
801
- </Physics>
802
-
803
- <OrbitControls />
804
- </Canvas>
805
- );
806
- }
807
- ```
808
-
809
- ### 5. Animation System
810
-
811
- ```tsx
812
- // lib/three/AnimationManager.ts
813
- import * as THREE from 'three';
814
- import gsap from 'gsap';
815
-
816
- interface AnimationConfig {
817
- duration?: number;
818
- ease?: string;
819
- delay?: number;
820
- repeat?: number;
821
- yoyo?: boolean;
822
- onComplete?: () => void;
823
- }
824
-
825
- export class AnimationManager {
826
- private mixer: THREE.AnimationMixer | null = null;
827
- private actions: Map<string, THREE.AnimationAction> = new Map();
828
- private timelines: Map<string, gsap.core.Timeline> = new Map();
829
-
830
- // GLTF animation setup
831
- public setupMixer(model: THREE.Object3D, animations: THREE.AnimationClip[]): void {
832
- this.mixer = new THREE.AnimationMixer(model);
833
-
834
- animations.forEach((clip) => {
835
- const action = this.mixer!.clipAction(clip);
836
- this.actions.set(clip.name, action);
837
- });
838
- }
839
-
840
- public playAnimation(name: string, options: { loop?: boolean; crossFade?: number } = {}): void {
841
- const action = this.actions.get(name);
842
- if (!action) return;
843
-
844
- const { loop = true, crossFade = 0.3 } = options;
845
-
846
- // Stop other actions with crossfade
847
- this.actions.forEach((a, n) => {
848
- if (n !== name && a.isRunning()) {
849
- a.fadeOut(crossFade);
850
- }
851
- });
852
-
853
- action.reset();
854
- action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, Infinity);
855
- action.clampWhenFinished = !loop;
856
- action.fadeIn(crossFade);
857
- action.play();
858
- }
859
-
860
- public update(delta: number): void {
861
- this.mixer?.update(delta);
862
- }
863
-
864
- // GSAP-based object animation
865
- public animateTo(
866
- object: THREE.Object3D,
867
- properties: Partial<{
868
- position: { x?: number; y?: number; z?: number };
869
- rotation: { x?: number; y?: number; z?: number };
870
- scale: { x?: number; y?: number; z?: number };
871
- }>,
872
- config: AnimationConfig = {}
873
- ): gsap.core.Tween {
874
- const { duration = 1, ease = 'power2.out', delay = 0, onComplete } = config;
875
-
876
- const targets: any[] = [];
877
- const props: any = {};
878
-
879
- if (properties.position) {
880
- targets.push(object.position);
881
- Object.assign(props, properties.position);
882
- }
883
- if (properties.rotation) {
884
- targets.push(object.rotation);
885
- Object.assign(props, properties.rotation);
886
- }
887
- if (properties.scale) {
888
- targets.push(object.scale);
889
- Object.assign(props, properties.scale);
890
- }
891
-
892
- return gsap.to(targets, {
893
- ...props,
894
- duration,
895
- ease,
896
- delay,
897
- onComplete,
898
- });
899
- }
900
-
901
- // Timeline-based complex animations
902
- public createTimeline(id: string): gsap.core.Timeline {
903
- const timeline = gsap.timeline({ paused: true });
904
- this.timelines.set(id, timeline);
905
- return timeline;
906
- }
907
-
908
- public playTimeline(id: string): void {
909
- this.timelines.get(id)?.play();
910
- }
911
-
912
- public dispose(): void {
913
- this.actions.clear();
914
- this.timelines.forEach((tl) => tl.kill());
915
- this.timelines.clear();
916
- }
917
- }
918
-
919
- // React hook for animations
920
- function useAnimation(meshRef: React.RefObject<THREE.Mesh>) {
921
- const [isAnimating, setIsAnimating] = useState(false);
922
-
923
- const animateIn = useCallback(() => {
924
- if (!meshRef.current || isAnimating) return;
925
-
926
- setIsAnimating(true);
927
-
928
- gsap.timeline()
929
- .fromTo(
930
- meshRef.current.scale,
931
- { x: 0, y: 0, z: 0 },
932
- { x: 1, y: 1, z: 1, duration: 0.5, ease: 'back.out(1.7)' }
933
- )
934
- .fromTo(
935
- meshRef.current.rotation,
936
- { y: -Math.PI },
937
- { y: 0, duration: 0.5, ease: 'power2.out' },
938
- '<'
939
- )
940
- .call(() => setIsAnimating(false));
941
- }, [isAnimating]);
942
-
943
- const animateOut = useCallback(() => {
944
- if (!meshRef.current || isAnimating) return;
945
-
946
- setIsAnimating(true);
947
-
948
- gsap.to(meshRef.current.scale, {
949
- x: 0,
950
- y: 0,
951
- z: 0,
952
- duration: 0.3,
953
- ease: 'back.in(1.7)',
954
- onComplete: () => setIsAnimating(false),
955
- });
956
- }, [isAnimating]);
957
-
958
- return { animateIn, animateOut, isAnimating };
959
- }
960
-
961
- // Animated component example
962
- function AnimatedObject() {
963
- const meshRef = useRef<THREE.Mesh>(null);
964
- const { animateIn, animateOut, isAnimating } = useAnimation(meshRef);
965
-
966
- useEffect(() => {
967
- animateIn();
968
- }, []);
969
-
970
- return (
971
- <mesh
972
- ref={meshRef}
973
- onClick={animateOut}
974
- scale={[0, 0, 0]}
975
- >
976
- <boxGeometry />
977
- <meshStandardMaterial color="#ff6b6b" />
978
- </mesh>
979
- );
980
- }
981
- ```
982
-
983
- ### 6. Performance Optimization
984
-
985
- ```tsx
986
- // lib/three/PerformanceOptimizer.ts
987
- import * as THREE from 'three';
988
- import { useThree, useFrame } from '@react-three/fiber';
989
- import { useEffect, useMemo, useRef } from 'react';
990
-
991
- // Level of Detail (LOD) component
992
- function LODMesh({ position }: { position: [number, number, number] }) {
993
- const lodRef = useRef<THREE.LOD>(null);
994
- const { camera } = useThree();
995
-
996
- const geometries = useMemo(() => ({
997
- high: new THREE.IcosahedronGeometry(1, 4), // 320 triangles
998
- medium: new THREE.IcosahedronGeometry(1, 2), // 80 triangles
999
- low: new THREE.IcosahedronGeometry(1, 1), // 20 triangles
1000
- }), []);
1001
-
1002
- const material = useMemo(
1003
- () => new THREE.MeshStandardMaterial({ color: '#4ecdc4' }),
1004
- []
1005
- );
1006
-
1007
- useEffect(() => {
1008
- if (!lodRef.current) return;
1009
-
1010
- const meshHigh = new THREE.Mesh(geometries.high, material);
1011
- const meshMedium = new THREE.Mesh(geometries.medium, material);
1012
- const meshLow = new THREE.Mesh(geometries.low, material);
1013
-
1014
- lodRef.current.addLevel(meshHigh, 0);
1015
- lodRef.current.addLevel(meshMedium, 10);
1016
- lodRef.current.addLevel(meshLow, 20);
1017
-
1018
- return () => {
1019
- geometries.high.dispose();
1020
- geometries.medium.dispose();
1021
- geometries.low.dispose();
1022
- material.dispose();
1023
- };
1024
- }, [geometries, material]);
1025
-
1026
- useFrame(() => {
1027
- lodRef.current?.update(camera);
1028
- });
1029
-
1030
- return <lod ref={lodRef} position={position} />;
1031
- }
1032
-
1033
- // Instanced mesh for many identical objects
1034
75
  function InstancedBoxes({ count = 1000 }: { count?: number }) {
1035
76
  const meshRef = useRef<THREE.InstancedMesh>(null);
1036
- const tempObject = useMemo(() => new THREE.Object3D(), []);
1037
- const tempColor = useMemo(() => new THREE.Color(), []);
77
+ const temp = useMemo(() => new THREE.Object3D(), []);
1038
78
 
1039
79
  useEffect(() => {
1040
- if (!meshRef.current) return;
1041
-
1042
- // Set up instance matrices and colors
1043
80
  for (let i = 0; i < count; i++) {
1044
- tempObject.position.set(
81
+ temp.position.set(
1045
82
  (Math.random() - 0.5) * 50,
1046
83
  (Math.random() - 0.5) * 50,
1047
84
  (Math.random() - 0.5) * 50
1048
85
  );
1049
- tempObject.rotation.set(
1050
- Math.random() * Math.PI,
1051
- Math.random() * Math.PI,
1052
- Math.random() * Math.PI
1053
- );
1054
- tempObject.scale.setScalar(0.5 + Math.random() * 0.5);
1055
- tempObject.updateMatrix();
1056
-
1057
- meshRef.current.setMatrixAt(i, tempObject.matrix);
1058
- meshRef.current.setColorAt(
1059
- i,
1060
- tempColor.setHSL(Math.random(), 0.7, 0.5)
1061
- );
1062
- }
1063
-
1064
- meshRef.current.instanceMatrix.needsUpdate = true;
1065
- if (meshRef.current.instanceColor) {
1066
- meshRef.current.instanceColor.needsUpdate = true;
1067
- }
1068
- }, [count, tempObject, tempColor]);
1069
-
1070
- // Animate instances
1071
- useFrame(({ clock }) => {
1072
- if (!meshRef.current) return;
1073
-
1074
- const time = clock.getElapsedTime();
1075
-
1076
- for (let i = 0; i < count; i++) {
1077
- meshRef.current.getMatrixAt(i, tempObject.matrix);
1078
- tempObject.matrix.decompose(
1079
- tempObject.position,
1080
- tempObject.quaternion,
1081
- tempObject.scale
1082
- );
1083
-
1084
- tempObject.rotation.x = time * 0.5 + i * 0.01;
1085
- tempObject.rotation.y = time * 0.3 + i * 0.01;
1086
- tempObject.updateMatrix();
1087
-
1088
- meshRef.current.setMatrixAt(i, tempObject.matrix);
86
+ temp.updateMatrix();
87
+ meshRef.current?.setMatrixAt(i, temp.matrix);
1089
88
  }
1090
-
1091
- meshRef.current.instanceMatrix.needsUpdate = true;
1092
- });
89
+ meshRef.current!.instanceMatrix.needsUpdate = true;
90
+ }, [count, temp]);
1093
91
 
1094
92
  return (
1095
- <instancedMesh ref={meshRef} args={[undefined, undefined, count]} castShadow>
93
+ <instancedMesh ref={meshRef} args={[undefined, undefined, count]}>
1096
94
  <boxGeometry args={[1, 1, 1]} />
1097
95
  <meshStandardMaterial />
1098
96
  </instancedMesh>
1099
97
  );
1100
98
  }
1101
-
1102
- // Frustum culling helper
1103
- function useFrustumCulling(meshRef: React.RefObject<THREE.Mesh>) {
1104
- const { camera } = useThree();
1105
- const frustum = useMemo(() => new THREE.Frustum(), []);
1106
- const projScreenMatrix = useMemo(() => new THREE.Matrix4(), []);
1107
-
1108
- useFrame(() => {
1109
- if (!meshRef.current) return;
1110
-
1111
- projScreenMatrix.multiplyMatrices(
1112
- camera.projectionMatrix,
1113
- camera.matrixWorldInverse
1114
- );
1115
- frustum.setFromProjectionMatrix(projScreenMatrix);
1116
-
1117
- // Check if mesh is in frustum
1118
- const isVisible = frustum.intersectsObject(meshRef.current);
1119
- meshRef.current.visible = isVisible;
1120
- });
1121
- }
1122
-
1123
- // Texture optimization
1124
- function useOptimizedTexture(url: string) {
1125
- const texture = useMemo(() => {
1126
- const loader = new THREE.TextureLoader();
1127
- const tex = loader.load(url);
1128
-
1129
- // Optimize texture settings
1130
- tex.minFilter = THREE.LinearMipmapLinearFilter;
1131
- tex.magFilter = THREE.LinearFilter;
1132
- tex.anisotropy = 4; // Balance quality and performance
1133
- tex.generateMipmaps = true;
1134
-
1135
- return tex;
1136
- }, [url]);
1137
-
1138
- useEffect(() => {
1139
- return () => texture.dispose();
1140
- }, [texture]);
1141
-
1142
- return texture;
1143
- }
1144
-
1145
- // GPU particle system
1146
- function ParticleSystem({ count = 10000 }: { count?: number }) {
1147
- const particlesRef = useRef<THREE.Points>(null);
1148
-
1149
- const [positions, velocities] = useMemo(() => {
1150
- const positions = new Float32Array(count * 3);
1151
- const velocities = new Float32Array(count * 3);
1152
-
1153
- for (let i = 0; i < count; i++) {
1154
- const i3 = i * 3;
1155
- positions[i3] = (Math.random() - 0.5) * 20;
1156
- positions[i3 + 1] = Math.random() * 20;
1157
- positions[i3 + 2] = (Math.random() - 0.5) * 20;
1158
-
1159
- velocities[i3] = (Math.random() - 0.5) * 0.02;
1160
- velocities[i3 + 1] = -Math.random() * 0.05;
1161
- velocities[i3 + 2] = (Math.random() - 0.5) * 0.02;
1162
- }
1163
-
1164
- return [positions, velocities];
1165
- }, [count]);
1166
-
1167
- useFrame(() => {
1168
- if (!particlesRef.current) return;
1169
-
1170
- const positions = particlesRef.current.geometry.attributes.position.array as Float32Array;
1171
-
1172
- for (let i = 0; i < count; i++) {
1173
- const i3 = i * 3;
1174
-
1175
- positions[i3] += velocities[i3];
1176
- positions[i3 + 1] += velocities[i3 + 1];
1177
- positions[i3 + 2] += velocities[i3 + 2];
1178
-
1179
- // Reset particle when it falls below ground
1180
- if (positions[i3 + 1] < 0) {
1181
- positions[i3 + 1] = 20;
1182
- }
1183
- }
1184
-
1185
- particlesRef.current.geometry.attributes.position.needsUpdate = true;
1186
- });
1187
-
1188
- return (
1189
- <points ref={particlesRef}>
1190
- <bufferGeometry>
1191
- <bufferAttribute
1192
- attach="attributes-position"
1193
- count={count}
1194
- array={positions}
1195
- itemSize={3}
1196
- />
1197
- </bufferGeometry>
1198
- <pointsMaterial
1199
- size={0.05}
1200
- color="#ffffff"
1201
- transparent
1202
- opacity={0.8}
1203
- sizeAttenuation
1204
- />
1205
- </points>
1206
- );
1207
- }
1208
99
  ```
1209
100
 
1210
- ## Use Cases
1211
-
1212
- ### Interactive Product Viewer
101
+ ### Custom Shader Material
1213
102
 
1214
103
  ```tsx
1215
- // components/ProductViewer.tsx
1216
- import { Canvas } from '@react-three/fiber';
1217
- import { useGLTF, OrbitControls, Environment, Html, useProgress } from '@react-three/drei';
1218
- import { Suspense, useState } from 'react';
1219
-
1220
- function Loader() {
1221
- const { progress } = useProgress();
1222
- return (
1223
- <Html center>
1224
- <div className="text-white text-xl">
1225
- Loading... {progress.toFixed(0)}%
1226
- </div>
1227
- </Html>
1228
- );
1229
- }
1230
-
1231
- interface ProductProps {
1232
- modelUrl: string;
1233
- hotspots: Array<{
1234
- id: string;
1235
- position: [number, number, number];
1236
- label: string;
1237
- description: string;
1238
- }>;
1239
- }
1240
-
1241
- function Product({ modelUrl, hotspots }: ProductProps) {
1242
- const { scene } = useGLTF(modelUrl);
1243
- const [activeHotspot, setActiveHotspot] = useState<string | null>(null);
1244
-
1245
- return (
1246
- <group>
1247
- <primitive object={scene} scale={1} />
1248
-
1249
- {hotspots.map((hotspot) => (
1250
- <group key={hotspot.id} position={hotspot.position}>
1251
- <mesh onClick={() => setActiveHotspot(hotspot.id)}>
1252
- <sphereGeometry args={[0.1, 16, 16]} />
1253
- <meshBasicMaterial
1254
- color={activeHotspot === hotspot.id ? '#ff6b6b' : '#4ecdc4'}
1255
- />
1256
- </mesh>
1257
-
1258
- {activeHotspot === hotspot.id && (
1259
- <Html distanceFactor={5}>
1260
- <div className="bg-white p-4 rounded-lg shadow-lg min-w-[200px]">
1261
- <h3 className="font-bold">{hotspot.label}</h3>
1262
- <p className="text-sm text-gray-600">{hotspot.description}</p>
1263
- </div>
1264
- </Html>
1265
- )}
1266
- </group>
1267
- ))}
1268
- </group>
1269
- );
1270
- }
1271
-
1272
- export function ProductViewer({ modelUrl, hotspots }: ProductProps) {
1273
- return (
1274
- <div className="w-full h-screen">
1275
- <Canvas camera={{ position: [0, 0, 5], fov: 50 }}>
1276
- <Suspense fallback={<Loader />}>
1277
- <Product modelUrl={modelUrl} hotspots={hotspots} />
1278
- <Environment preset="studio" />
1279
- </Suspense>
1280
-
1281
- <OrbitControls
1282
- enablePan={false}
1283
- minDistance={2}
1284
- maxDistance={10}
1285
- autoRotate
1286
- autoRotateSpeed={0.5}
1287
- />
1288
- </Canvas>
1289
- </div>
1290
- );
1291
- }
104
+ const GradientMaterial = shaderMaterial(
105
+ { uTime: 0, uColorA: new THREE.Color('#ff6b6b'), uColorB: new THREE.Color('#4ecdc4') },
106
+ // Vertex shader
107
+ `varying vec2 vUv;
108
+ void main() {
109
+ vUv = uv;
110
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
111
+ }`,
112
+ // Fragment shader
113
+ `uniform float uTime;
114
+ uniform vec3 uColorA;
115
+ uniform vec3 uColorB;
116
+ varying vec2 vUv;
117
+ void main() {
118
+ vec3 color = mix(uColorA, uColorB, vUv.y + sin(uTime) * 0.1);
119
+ gl_FragColor = vec4(color, 1.0);
120
+ }`
121
+ );
122
+ extend({ GradientMaterial });
1292
123
  ```
1293
124
 
1294
125
  ## Best Practices
1295
126
 
1296
- ### Do's
1297
-
1298
- - Use React Three Fiber for React applications
1299
- - Dispose of geometries, materials, and textures when unmounting
1300
- - Use instancing for many identical objects
1301
- - Implement LOD for complex scenes
1302
- - Use texture atlases to reduce draw calls
1303
- - Enable shadow maps only when needed
1304
- - Use object pooling for frequently created/destroyed objects
1305
- - Optimize geometry with BufferGeometry
1306
- - Use compressed textures (KTX2, Basis)
1307
- - Profile performance with Chrome DevTools and Spector.js
1308
-
1309
- ### Don'ts
1310
-
1311
- - Don't create new geometries/materials in render loops
1312
- - Don't use too many lights (limit to 3-4 dynamic lights)
1313
- - Don't skip frustum culling for large scenes
1314
- - Don't forget to update instanceMatrix when animating instanced meshes
1315
- - Don't use transparent materials unless necessary
1316
- - Don't load uncompressed high-resolution textures
1317
- - Don't forget to set power-of-two texture dimensions
1318
- - Don't ignore memory leaks from undisposed resources
1319
- - Don't use complex shaders without fallbacks
1320
- - Don't skip mobile optimization and testing
1321
-
1322
- ## References
1323
-
1324
- - [Three.js Documentation](https://threejs.org/docs/)
1325
- - [React Three Fiber](https://docs.pmnd.rs/react-three-fiber/)
1326
- - [Drei Helpers](https://github.com/pmndrs/drei)
1327
- - [Three.js Fundamentals](https://threejs.org/manual/)
1328
- - [WebGL Best Practices](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices)
127
+ | Do | Avoid |
128
+ |----|-------|
129
+ | Use React Three Fiber for React apps | Creating geometries/materials in render loops |
130
+ | Dispose geometries, materials, textures on unmount | Too many dynamic lights (limit to 3-4) |
131
+ | Use instancing for many identical objects | Skipping frustum culling in large scenes |
132
+ | Implement LOD for complex scenes | Uncompressed high-resolution textures |
133
+ | Cap pixel ratio at 2: `Math.min(window.devicePixelRatio, 2)` | Transparent materials unless necessary |
134
+ | Use compressed textures (KTX2, Basis) | Forgetting to update instanceMatrix after changes |