Files
Portfolio/blog-posts/71-threejs-react-three-fiber.md
damjan_savicandClaude Opus 4.5 43484c5023 Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics
- Add .env.example for environment configuration
- Add accessibility and lighthouse audit scripts
- Remove obsolete SEO reports and temporary files
- Remove dev-dist build artifacts and backup files
- Remove unused portrait images (moved/consolidated elsewhere)
- Update contact form and component improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:42:11 +01:00

613 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Three.js & React Three Fiber für 3D Web
**Meta-Description:** 3D Webentwicklung mit Three.js und React Three Fiber. WebGL/WebGPU, interaktive Szenen und Performance-Optimierung.
**Keywords:** Three.js, React Three Fiber, 3D Web, WebGL, WebGPU, R3F, Drei, Interactive 3D
---
## Einführung
**Three.js** dominiert die 3D-Webentwicklung mit 270x mehr Downloads als Alternativen. **React Three Fiber** (R3F) bringt die deklarative React-Syntax in die 3D-Welt perfekt für moderne Web-Projekte mit interaktiven Visualisierungen.
---
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────┐
│ THREE.JS / R3F ARCHITECTURE │
├─────────────────────────────────────────────────────────────┤
│ │
│ Application Layer: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ React Components / Hooks / State │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ R3F Layer: ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ React Three Fiber (React Renderer for Three.js) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Canvas │ │ Hooks │ │ Events │ │ │
│ │ │ (Root) │ │ (Frame) │ │(Pointer)│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ Drei (Helpers): │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ OrbitControls | Environment | Text3D | useGLTF │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ Three.js Core: ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Scene | Camera | Renderer | Lights | Geometries │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ Graphics API: ▼ │
│ ┌───────────────────┬────────────────────┐ │
│ │ WebGL 2 │ WebGPU │ │
│ │ (Supported) │ (2026 Ready) │ │
│ └───────────────────┴────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## Setup & Installation
```bash
# React Three Fiber + Drei (Helpers)
npm install three @react-three/fiber @react-three/drei
# TypeScript Types
npm install -D @types/three
# Optional: Post-Processing, Physics
npm install @react-three/postprocessing
npm install @react-three/cannon
```
```typescript
// next.config.js (für Next.js)
const nextConfig = {
transpilePackages: ['three'],
webpack: (config) => {
config.externals.push({
'sharp': 'commonjs sharp',
'canvas': 'commonjs canvas',
});
return config;
}
};
```
---
## Basic Scene mit R3F
```tsx
// components/Scene.tsx
'use client';
import { Canvas } from '@react-three/fiber';
import { OrbitControls, Environment, PerspectiveCamera } from '@react-three/drei';
import { Suspense } from 'react';
export function Scene() {
return (
<Canvas>
<PerspectiveCamera makeDefault position={[0, 2, 5]} />
<OrbitControls enableDamping dampingFactor={0.05} />
<Suspense fallback={<LoadingBox />}>
<Environment preset="sunset" />
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} intensity={1} castShadow />
<RotatingBox position={[-1.5, 0, 0]} />
<FloatingSphere position={[1.5, 0, 0]} />
<Floor />
</Suspense>
</Canvas>
);
}
function LoadingBox() {
return (
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshBasicMaterial color="gray" wireframe />
</mesh>
);
}
```
---
## Interaktive Objekte
```tsx
// components/InteractiveObjects.tsx
'use client';
import { useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber';
import { MeshWobbleMaterial, RoundedBox, Text } from '@react-three/drei';
import * as THREE from 'three';
interface BoxProps {
position: [number, number, number];
}
export function RotatingBox({ position }: BoxProps) {
const meshRef = useRef<THREE.Mesh>(null);
const [hovered, setHovered] = useState(false);
const [clicked, setClicked] = useState(false);
useFrame((state, delta) => {
if (meshRef.current) {
meshRef.current.rotation.x += delta * 0.5;
meshRef.current.rotation.y += delta * 0.3;
// Hover Animation
const scale = hovered ? 1.2 : 1;
meshRef.current.scale.lerp(new THREE.Vector3(scale, scale, scale), 0.1);
}
});
return (
<mesh
ref={meshRef}
position={position}
onClick={() => setClicked(!clicked)}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial
color={clicked ? '#ff6b6b' : hovered ? '#4ecdc4' : '#45b7d1'}
metalness={0.5}
roughness={0.2}
/>
</mesh>
);
}
export function FloatingSphere({ position }: BoxProps) {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state) => {
if (meshRef.current) {
// Floating Animation
meshRef.current.position.y = position[1] + Math.sin(state.clock.elapsedTime) * 0.3;
}
});
return (
<mesh ref={meshRef} position={position}>
<sphereGeometry args={[0.7, 32, 32]} />
<MeshWobbleMaterial
color="#9b59b6"
metalness={0.8}
roughness={0.1}
factor={0.5} // Wobble Intensity
speed={2} // Wobble Speed
/>
</mesh>
);
}
export function Floor() {
return (
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]} receiveShadow>
<planeGeometry args={[20, 20]} />
<meshStandardMaterial color="#1a1a2e" metalness={0.2} roughness={0.8} />
</mesh>
);
}
```
---
## 3D Model Loading (GLTF)
```tsx
// components/Model.tsx
'use client';
import { useRef, useEffect } from 'react';
import { useGLTF, useAnimations, Clone } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface ModelProps {
url: string;
position?: [number, number, number];
scale?: number;
animate?: boolean;
}
export function Model({ url, position = [0, 0, 0], scale = 1, animate = true }: ModelProps) {
const group = useRef<THREE.Group>(null);
const { scene, animations } = useGLTF(url);
const { actions, names } = useAnimations(animations, group);
useEffect(() => {
// Erste Animation abspielen
if (animate && names.length > 0) {
actions[names[0]]?.play();
}
}, [actions, names, animate]);
// Shadow Setup
useEffect(() => {
scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
}, [scene]);
return (
<group ref={group} position={position} scale={scale}>
<Clone object={scene} />
</group>
);
}
// Preload für bessere Performance
useGLTF.preload('/models/robot.glb');
// Mit Draco Compression
export function CompressedModel({ url }: { url: string }) {
const { scene } = useGLTF(url, true); // true = Draco Loader
return <primitive object={scene} />;
}
```
---
## Custom Shaders
```tsx
// components/CustomShader.tsx
'use client';
import { useRef, useMemo } from 'react';
import { useFrame, extend } from '@react-three/fiber';
import { shaderMaterial } from '@react-three/drei';
import * as THREE from 'three';
// Custom Shader Material
const GradientMaterial = shaderMaterial(
// Uniforms
{
uTime: 0,
uColor1: new THREE.Color('#ff0000'),
uColor2: new THREE.Color('#0000ff'),
uIntensity: 1.0
},
// Vertex Shader
`
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
// Fragment Shader
`
uniform float uTime;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uIntensity;
varying vec2 vUv;
varying vec3 vPosition;
void main() {
// Animated Gradient
float mixValue = sin(vUv.y * 3.14159 + uTime) * 0.5 + 0.5;
vec3 color = mix(uColor1, uColor2, mixValue);
// Wave Effect
float wave = sin(vPosition.x * 5.0 + uTime * 2.0) * 0.1;
color += wave * uIntensity;
gl_FragColor = vec4(color, 1.0);
}
`
);
// Extend für JSX Support
extend({ GradientMaterial });
// TypeScript Declaration
declare global {
namespace JSX {
interface IntrinsicElements {
gradientMaterial: any;
}
}
}
export function ShaderSphere() {
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}
uColor1="#ff6b6b"
uColor2="#4ecdc4"
uIntensity={0.5}
/>
</mesh>
);
}
```
---
## Post-Processing Effects
```tsx
// components/PostProcessing.tsx
'use client';
import { EffectComposer, Bloom, ChromaticAberration, Vignette } from '@react-three/postprocessing';
import { BlendFunction } from 'postprocessing';
export function Effects() {
return (
<EffectComposer>
{/* Bloom für leuchtende Effekte */}
<Bloom
intensity={1.5}
luminanceThreshold={0.9}
luminanceSmoothing={0.025}
mipmapBlur
/>
{/* Chromatische Aberration */}
<ChromaticAberration
blendFunction={BlendFunction.NORMAL}
offset={[0.002, 0.002]}
/>
{/* Vignette */}
<Vignette
offset={0.3}
darkness={0.5}
blendFunction={BlendFunction.NORMAL}
/>
</EffectComposer>
);
}
// Verwendung
function SceneWithEffects() {
return (
<Canvas>
<Scene />
<Effects />
</Canvas>
);
}
```
---
## Physics mit Cannon
```tsx
// components/PhysicsScene.tsx
'use client';
import { Physics, usePlane, useBox, useSphere } from '@react-three/cannon';
import { useRef } from 'react';
import * as THREE from 'three';
export function PhysicsScene() {
return (
<Physics gravity={[0, -9.81, 0]}>
<PhysicsGround />
<PhysicsBox position={[0, 5, 0]} />
<PhysicsBox position={[0.5, 7, 0]} />
<PhysicsBall position={[-0.5, 10, 0]} />
</Physics>
);
}
function PhysicsGround() {
const [ref] = usePlane<THREE.Mesh>(() => ({
rotation: [-Math.PI / 2, 0, 0],
position: [0, 0, 0]
}));
return (
<mesh ref={ref} receiveShadow>
<planeGeometry args={[20, 20]} />
<meshStandardMaterial color="#333" />
</mesh>
);
}
function PhysicsBox({ position }: { position: [number, number, number] }) {
const [ref] = useBox<THREE.Mesh>(() => ({
mass: 1,
position,
args: [1, 1, 1]
}));
return (
<mesh ref={ref} castShadow>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="#ff6b6b" />
</mesh>
);
}
function PhysicsBall({ position }: { position: [number, number, number] }) {
const [ref] = useSphere<THREE.Mesh>(() => ({
mass: 2,
position,
args: [0.5]
}));
return (
<mesh ref={ref} castShadow>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color="#4ecdc4" />
</mesh>
);
}
```
---
## Performance Optimierung
```tsx
// components/OptimizedScene.tsx
'use client';
import { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import { Instances, Instance, Detailed, useGLTF } from '@react-three/drei';
import * as THREE from 'three';
// Instancing für viele gleiche Objekte
export function InstancedCubes({ count = 1000 }) {
const positions = useMemo(() => {
return Array.from({ length: count }, () => ({
position: [
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20
] as [number, number, number],
rotation: [
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
] as [number, number, number],
scale: 0.5 + Math.random() * 0.5
}));
}, [count]);
return (
<Instances limit={count} castShadow receiveShadow>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="#45b7d1" />
{positions.map((props, i) => (
<Instance
key={i}
position={props.position}
rotation={props.rotation}
scale={props.scale}
/>
))}
</Instances>
);
}
// Level of Detail (LOD)
export function LODModel() {
return (
<Detailed distances={[0, 10, 25, 50]}>
{/* Höchste Qualität (nah) */}
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<meshStandardMaterial color="red" />
</mesh>
{/* Mittlere Qualität */}
<mesh>
<sphereGeometry args={[1, 32, 32]} />
<meshStandardMaterial color="red" />
</mesh>
{/* Niedrige Qualität */}
<mesh>
<sphereGeometry args={[1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
{/* Sehr niedrige Qualität (weit) */}
<mesh>
<sphereGeometry args={[1, 8, 8]} />
<meshStandardMaterial color="red" />
</mesh>
</Detailed>
);
}
// Lazy Loading
import { lazy, Suspense } from 'react';
const HeavyModel = lazy(() => import('./HeavyModel'));
export function LazyScene() {
return (
<Suspense fallback={<LoadingIndicator />}>
<HeavyModel />
</Suspense>
);
}
```
---
## Best Practices
| Aspect | Recommendation |
|--------|----------------|
| **Geometry** | Instancing für viele gleiche Objekte |
| **Textures** | Compressed (WebP), Power of 2 |
| **Models** | Draco compression, LOD |
| **Shadows** | Nur wo nötig, Shadow Map Size |
| **Animations** | useFrame statt setInterval |
| **State** | Zustand außerhalb des Render Loops |
| **Cleanup** | dispose() für Geometries/Materials |
---
## Fazit
Three.js + React Three Fiber bietet:
1. **Deklarative Syntax**: React-Komponenten für 3D
2. **Ecosystem**: Drei, Cannon, Postprocessing
3. **WebGPU Ready**: Zukunftssicher mit WebGPU Support
4. **Performance**: Instancing, LOD, Lazy Loading
Die beste Kombination für 3D im Web.
---
## Bildprompts
1. "Interactive 3D scene with rotating objects, React Three Fiber demo"
2. "WebGL shader effect with gradient colors, custom material"
3. "Physics simulation with falling objects, cannon.js integration"
---
## Quellen
- [React Three Fiber Documentation](https://docs.pmnd.rs/react-three-fiber)
- [Three.js Journey](https://threejs-journey.com/)
- [Drei Helpers](https://github.com/pmndrs/drei)
- [What's New in Three.js 2026](https://www.utsubo.com/blog/threejs-2026-what-changed)