Skip to main content 首页 创作者 inugamidev ultrathink-oss threejs
threejs Three.js and React Three Fiber (R3F) 3D development — scene setup, lighting, materials, physics, performance, and interactive 3D experiences
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/InugamiDev/ultrathink-oss --skill threejs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... 同仓库更多 Skills Unified design foundations — design system architecture, tokens, component specs, visual principles, creative vision, figma integration, plus brand design system loader (66 real brands via DESIGN.md). Absorbs design, design-system, design-systems, design-principles, design-router, creative-vision, figma, design-md.
Render, summarize, and present markdown documents and structured content in multiple output modes
Ultra UI skill - combines Google's DESIGN.md spec (machine-readable design tokens) with the ui-ux-pro-max knowledge base (91 styles, 161 palettes, 73 font pairings, 161 products, 104 UX guidelines, 25 chart types). Generates lint-clean DESIGN.md files, validates token references and WCAG contrast, exports Tailwind/DTCG tokens, and diffs design systems version-over-version.
name threejs description Three.js and React Three Fiber (R3F) 3D development — scene setup, lighting, materials, physics, performance, and interactive 3D experiences layer domain category frontend triggers ["three.js","threejs","3d","react three fiber","R3F","WebGL","3D scene","shader","GLSL","drei","3D model","GLTF"] inputs ["3D scene requirements or visual specs","Performance optimization for 3D rendering","Interactive 3D feature requests","Shader or material questions"] outputs ["React Three Fiber scene implementations","Custom shaders and materials","Optimized 3D rendering pipelines","Interactive 3D component patterns"] linksTo ["react","animation","typescript-frontend"] linkedFrom ["code-writer","ui-designer"] preferredNextSkills ["animation","react","typescript-frontend"] fallbackSkills [] riskLevel low memoryReadPolicy selective memoryWritePolicy none sideEffects []
Three.js & React Three Fiber (R3F)
Purpose
Provide expert guidance on building 3D experiences for the web using Three.js and React Three Fiber (R3F). Covers scene setup, lighting, materials, model loading, physics, shaders, performance optimization, and integration with React applications.
Key Patterns
R3F Scene Setup
Basic scene with Canvas:
import { Canvas } from '@react-three/fiber' ;
import { OrbitControls , Environment , ContactShadows } from '@react-three/drei' ;
function Scene3D ( ) {
return (
<div className ="w-full h-[600px] rounded-xl overflow-hidden" >
<Canvas
camera ={{ position: [0 , 2 , 5 ], fov: 50 }}
dpr ={[1, 2 ]} // Responsive pixel ratio
gl ={{ antialias: true , alpha: true }}
shadows
>
{/* Lighting */}
<ambientLight intensity ={0.4} />
<directionalLight
= , ]}
=
= ]}
/>
{/* Environment map for reflections */}
{/* Scene content */}
{/* Ground shadows */}
{/* Camera controls */}
);
}
position
{[5,
5
5
intensity
{1}
castShadow
shadow-mapSize
{[1024,
1024
<Environment preset ="city" />
<mesh castShadow receiveShadow >
<boxGeometry args ={[1, 1 , 1 ]} />
<meshStandardMaterial color ="#3b82f6" roughness ={0.3} metalness ={0.1} />
</mesh >
<ContactShadows
position ={[0, -0.5 , 0 ]}
opacity ={0.4}
blur ={2}
far ={4}
/>
<OrbitControls
enableDamping
dampingFactor ={0.05}
minDistance ={2}
maxDistance ={10}
maxPolarAngle ={Math.PI / 2 }
/>
</Canvas >
</div >
Loading 3D Models import { useGLTF, useAnimations, Center } from '@react-three/drei' ;
import { useEffect, useRef } from 'react' ;
import * as THREE from 'three' ;
useGLTF.preload ('/models/robot.glb' );
function RobotModel ({ action = 'idle' }: { action?: string } ) {
const group = useRef<THREE .Group >(null );
const { scene, animations } = useGLTF ('/models/robot.glb' );
const { actions } = useAnimations (animations, group);
useEffect (() => {
const current = actions[action];
current?.reset ().fadeIn (0.3 ).play ();
return () => { current?.fadeOut (0.3 ); };
}, [action, actions]);
return (
<Center >
<group ref ={group} >
<primitive object ={scene} />
</group >
</Center >
);
}
Generate typed component from GLTF (gltfjsx):
npx gltfjsx model.glb --types --transform
Custom Materials & Shaders import { shaderMaterial } from '@react-three/drei' ;
import { extend, useFrame } from '@react-three/fiber' ;
import { useRef } from 'react' ;
import * as THREE from 'three' ;
const GradientMaterial = shaderMaterial (
{
uTime : 0 ,
uColor1 : new THREE .Color ('#3b82f6' ),
uColor2 : new THREE .Color ('#8b5cf6' ),
uNoiseScale : 2.0 ,
},
`
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
` ,
`
uniform float uTime;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uNoiseScale;
varying vec2 vUv;
// Simple noise function
float noise(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
void main() {
float n = noise(vUv * uNoiseScale + uTime * 0.2);
vec3 color = mix(uColor1, uColor2, vUv.y + n * 0.2);
gl_FragColor = vec4(color, 1.0);
}
`
);
extend ({ GradientMaterial });
declare module '@react-three/fiber' {
interface ThreeElements {
gradientMaterial : JSX .IntrinsicElements ['meshStandardMaterial' ] & {
uTime ?: number ;
uColor1 ?: THREE .Color ;
uColor2 ?: THREE .Color ;
uNoiseScale ?: number ;
};
}
}
function GradientSphere ( ) {
const materialRef = useRef<any >(null );
useFrame ((state ) => {
if (materialRef.current ) {
materialRef.current .uTime = state.clock .elapsedTime ;
}
});
return (
<mesh >
<sphereGeometry args ={[1, 64 , 64 ]} />
<gradientMaterial ref ={materialRef} />
</mesh >
);
}
Animation with useFrame import { useFrame } from '@react-three/fiber' ;
import { useRef } from 'react' ;
import * as THREE from 'three' ;
function FloatingCube ( ) {
const meshRef = useRef<THREE .Mesh >(null );
useFrame ((state, delta ) => {
if (!meshRef.current ) return ;
meshRef.current .position .y = Math .sin (state.clock .elapsedTime * 0.8 ) * 0.3 ;
meshRef.current .rotation .y += delta * 0.5 ;
const targetScale = meshRef.current .userData .hovered ? 1.1 : 1 ;
meshRef.current .scale .lerp (
new THREE .Vector3 (targetScale, targetScale, targetScale),
0.1
);
});
return (
<mesh
ref ={meshRef}
onPointerEnter ={(e) => {
e.stopPropagation();
e.object.userData.hovered = true;
document.body.style.cursor = 'pointer';
}}
onPointerLeave={(e) => {
e.object.userData.hovered = false;
document.body.style.cursor = 'auto';
}}
>
<boxGeometry args ={[1, 1 , 1 ]} />
<meshStandardMaterial color ="#3b82f6" />
</mesh >
);
}
Physics with Rapier import { Physics , RigidBody , CuboidCollider } from '@react-three/rapier' ;
function PhysicsScene ( ) {
return (
<Physics gravity ={[0, -9.81 , 0 ]} debug ={false} >
{/* Falling boxes */}
{Array.from({ length: 10 }).map((_, i) => (
<RigidBody
key ={i}
position ={[Math.random() * 4 - 2 , 5 + i * 1.5 , Math.random () * 4 - 2 ]}
restitution ={0.3}
>
<mesh castShadow >
<boxGeometry args ={[0.5, 0.5 , 0.5 ]} />
<meshStandardMaterial color ={ `hsl (${i * 36 }, 70 %, 60 %)`} />
</mesh >
</RigidBody >
))}
{/* Ground plane */}
<RigidBody type ="fixed" >
<CuboidCollider args ={[10, 0.1 , 10 ]} position ={[0, -0.1 , 0 ]} />
<mesh receiveShadow position ={[0, -0.1 , 0 ]}>
<boxGeometry args ={[20, 0.2 , 20 ]} />
<meshStandardMaterial color ="#e2e8f0" />
</mesh >
</RigidBody >
</Physics >
);
}
Post-Processing import { EffectComposer , Bloom , ChromaticAberration , Vignette } from '@react-three/postprocessing' ;
import { BlendFunction } from 'postprocessing' ;
function PostProcessingScene ( ) {
return (
<Canvas >
{/* Scene content */}
<SceneContent />
{/* Post-processing effects */}
<EffectComposer >
<Bloom
intensity ={0.5}
luminanceThreshold ={0.8}
luminanceSmoothing ={0.3}
/>
<ChromaticAberration
blendFunction ={BlendFunction.NORMAL}
offset ={[0.001, 0.001 ]}
/>
<Vignette
eskil ={false}
offset ={0.1}
darkness ={0.5}
/>
</EffectComposer >
</Canvas >
);
}
Responsive 3D import { useThree } from '@react-three/fiber' ;
import { useEffect } from 'react' ;
function ResponsiveCamera ( ) {
const { camera, size } = useThree ();
useEffect (() => {
if (size.width < 768 ) {
camera.position .set (0 , 3 , 8 );
} else {
camera.position .set (0 , 2 , 5 );
}
camera.updateProjectionMatrix ();
}, [size, camera]);
return null ;
}
<Canvas
dpr={[1 , Math .min (window .devicePixelRatio , 2 )]}
performance={{ min : 0.5 }}
>
HTML Overlays in 3D import { Html } from '@react-three/drei' ;
function AnnotatedModel ( ) {
return (
<group >
<mesh >
<sphereGeometry args ={[1, 32 , 32 ]} />
<meshStandardMaterial color ="#3b82f6" />
</mesh >
{/* HTML label positioned in 3D space */}
<Html
position ={[1.5, 0.5 , 0 ]}
center
distanceFactor ={8}
className ="pointer-events-none"
>
<div className ="px-4 py-2 rounded-lg bg-white/90 backdrop-blur-sm shadow-md text-sm whitespace-nowrap" >
Click to interact
</div >
</Html >
</group >
);
}
Performance Optimization
Geometry & Mesh
Instance meshes — Use <instancedMesh> for many identical objects (trees, particles, buildings):
function Particles ({ count = 1000 } ) {
const meshRef = useRef<THREE .InstancedMesh >(null );
const tempObject = new THREE .Object3D ();
useEffect (() => {
for (let i = 0 ; i < count; i++) {
tempObject.position .set (
(Math .random () - 0.5 ) * 20 ,
(Math .random () - 0.5 ) * 20 ,
(Math .random () - 0.5 ) * 20
);
tempObject.scale .setScalar (Math .random () * 0.1 + 0.05 );
tempObject.updateMatrix ();
meshRef.current !.setMatrixAt (i, tempObject.matrix );
}
meshRef.current !.instanceMatrix .needsUpdate = true ;
}, [count]);
return (
<instancedMesh ref ={meshRef} args ={[undefined, undefined , count ]}>
<sphereGeometry args ={[1, 8 , 8 ]} />
<meshStandardMaterial color ="#3b82f6" />
</instancedMesh >
);
}
Merged geometries — Use <Merged> from Drei for static scenes.
LOD (Level of Detail) — Show lower-poly models at distance:
import { Detailed } from '@react-three/drei' ;
<Detailed distances ={[0, 20 , 50 ]}>
<HighPolyModel />
<MedPolyModel />
<LowPolyModel />
</Detailed >
Rendering
DPR capping — dpr={[1, 2]} caps pixel ratio at 2x (Retina is enough).
Adaptive performance — Use <PerformanceMonitor> to auto-adjust quality:
import { PerformanceMonitor } from '@react-three/drei' ;
<Canvas >
<PerformanceMonitor
onIncline ={() => setQuality('high')}
onDecline={() => setQuality('low')}
flipflops={3}
onFallback={() => setQuality('low')}
>
<Scene quality ={quality} />
</PerformanceMonitor >
</Canvas >
Frustum culling — Enabled by default. Ensure large objects have correct bounding boxes.
Texture optimization — Use KTX2/Basis compressed textures, power-of-two sizes.
Dispose resources — Clean up geometries, materials, and textures when unmounting.
Best Practices
R3F over vanilla Three.js — In React apps, always use R3F for proper lifecycle management.
Drei for common needs — Check @react-three/drei before building from scratch (controls, loaders, helpers).
Suspend loading — Use useGLTF / useTexture with React Suspense for loading states.
Delta-based animation — Use delta from useFrame for frame-rate independent animation.
No state in useFrame — Read state via refs, not useState. Setting state in useFrame causes re-renders at 60fps.
Preload assets — useGLTF.preload() and useTexture.preload() at module level.
Compress models — Use gltf-transform or Draco compression. Target < 1MB for hero models.
Test on mobile — 3D performance varies massively. Always test on real devices.
Fallback for no WebGL — Detect WebGL support and show a static image fallback.
Accessibility — Add aria-label to the Canvas container, provide alternative content for screen readers.
Common Pitfalls Pitfall Problem Fix setState in useFrame Re-render loop at 60fps Use useRef for mutable values in animation loop New objects in useFrame GC thrashing Create objects outside the loop, reuse with .set() Missing dispose GPU memory leaks Dispose geometries, materials, textures on unmount Uncompressed models Huge download, slow parse Draco/Meshopt compression, gltf-transform Too many draw calls Low FPS Instance meshes, merge static geometry, reduce materials Full-res shadows Expensive Limit shadow map size, use contact shadows for simple scenes Missing Suspense Canvas shows nothing during load Wrap model components in <Suspense fallback={<Loader />}> Canvas re-creating Entire scene rebuilds Keep Canvas mounted, change content not the canvas