Menu
Zurück zum Blog
2 min read
Web Development

Three.js & React Three Fiber für 3D Web

3D Webentwicklung mit Three.js und React Three Fiber. WebGL/WebGPU, interaktive Szenen und Performance-Optimierung.

Three.jsReact Three Fiber3D WebWebGLWebGPUR3F
Three.js & React Three Fiber für 3D Web

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

# 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
// 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

// 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

// 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)

// 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

// 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

// 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

// 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

// 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

AspectRecommendation
**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