- 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>
635 lines
17 KiB
Markdown
635 lines
17 KiB
Markdown
# WebGPU: Next-Gen Browser Graphics
|
|
|
|
**Meta-Description:** WebGPU für 3D-Grafik im Browser. Performance-Vergleich zu WebGL, Compute Shaders und Three.js WebGPU Renderer.
|
|
|
|
**Keywords:** WebGPU, WebGL, 3D Graphics, Compute Shaders, GPU Programming, Three.js, Browser Graphics
|
|
|
|
---
|
|
|
|
## Einführung
|
|
|
|
**WebGPU** ist der Nachfolger von WebGL und bringt moderne GPU-Architektur in den Browser. Mit **Compute Shaders**, besserer Performance und Support in allen Major Browsern (seit 2025) ist WebGPU bereit für Production.
|
|
|
|
---
|
|
|
|
## WebGPU vs WebGL
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ WEBGPU VS WEBGL COMPARISON │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Architecture: │
|
|
│ │
|
|
│ WebGL (2011): │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ JavaScript │ │
|
|
│ │ ↓ │ │
|
|
│ │ OpenGL ES (State Machine) │ │
|
|
│ │ ↓ │ │
|
|
│ │ Driver Translation Layer │ │
|
|
│ │ ↓ │ │
|
|
│ │ GPU │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ WebGPU (2023): │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ JavaScript / WASM │ │
|
|
│ │ ↓ │ │
|
|
│ │ WebGPU API (Modern, Low-Level) │ │
|
|
│ │ ↓ │ │
|
|
│ │ Native GPU APIs (Vulkan/Metal/D3D12) │ │
|
|
│ │ ↓ │ │
|
|
│ │ GPU │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Performance Comparison: │
|
|
│ ├── Draw Calls: WebGPU 10x faster (Render Bundles) │
|
|
│ ├── Compute: WebGPU has Compute Shaders (WebGL: none) │
|
|
│ ├── Multi-threading: WebGPU supports parallel encoding │
|
|
│ └── Memory: WebGPU explicit resource management │
|
|
│ │
|
|
│ Browser Support (2026): │
|
|
│ ├── Chrome/Edge: ✅ (since April 2023) │
|
|
│ ├── Firefox: ✅ (since July 2025) │
|
|
│ ├── Safari: ✅ (since June 2025, Safari 26) │
|
|
│ └── Mobile: Partial (Chrome Android, Safari iOS 26) │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Basic WebGPU Setup
|
|
|
|
```typescript
|
|
// lib/webgpu-setup.ts
|
|
|
|
async function initWebGPU(): Promise<{
|
|
device: GPUDevice;
|
|
context: GPUCanvasContext;
|
|
format: GPUTextureFormat;
|
|
}> {
|
|
// Check Support
|
|
if (!navigator.gpu) {
|
|
throw new Error('WebGPU not supported');
|
|
}
|
|
|
|
// Request Adapter
|
|
const adapter = await navigator.gpu.requestAdapter({
|
|
powerPreference: 'high-performance'
|
|
});
|
|
|
|
if (!adapter) {
|
|
throw new Error('No GPU adapter found');
|
|
}
|
|
|
|
// Request Device
|
|
const device = await adapter.requestDevice({
|
|
requiredFeatures: [],
|
|
requiredLimits: {}
|
|
});
|
|
|
|
// Canvas Context
|
|
const canvas = document.querySelector('canvas')!;
|
|
const context = canvas.getContext('webgpu')!;
|
|
|
|
const format = navigator.gpu.getPreferredCanvasFormat();
|
|
|
|
context.configure({
|
|
device,
|
|
format,
|
|
alphaMode: 'premultiplied'
|
|
});
|
|
|
|
return { device, context, format };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Triangle Rendering
|
|
|
|
```typescript
|
|
// webgpu-triangle.ts
|
|
|
|
const vertexShaderCode = /* wgsl */ `
|
|
struct VertexOutput {
|
|
@builtin(position) position: vec4f,
|
|
@location(0) color: vec4f,
|
|
}
|
|
|
|
@vertex
|
|
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
var positions = array<vec2f, 3>(
|
|
vec2f( 0.0, 0.5), // Top
|
|
vec2f(-0.5, -0.5), // Bottom Left
|
|
vec2f( 0.5, -0.5) // Bottom Right
|
|
);
|
|
|
|
var colors = array<vec4f, 3>(
|
|
vec4f(1.0, 0.0, 0.0, 1.0), // Red
|
|
vec4f(0.0, 1.0, 0.0, 1.0), // Green
|
|
vec4f(0.0, 0.0, 1.0, 1.0) // Blue
|
|
);
|
|
|
|
var output: VertexOutput;
|
|
output.position = vec4f(positions[vertexIndex], 0.0, 1.0);
|
|
output.color = colors[vertexIndex];
|
|
return output;
|
|
}
|
|
`;
|
|
|
|
const fragmentShaderCode = /* wgsl */ `
|
|
@fragment
|
|
fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
return color;
|
|
}
|
|
`;
|
|
|
|
async function renderTriangle() {
|
|
const { device, context, format } = await initWebGPU();
|
|
|
|
// Shader Module
|
|
const shaderModule = device.createShaderModule({
|
|
code: vertexShaderCode + fragmentShaderCode
|
|
});
|
|
|
|
// Pipeline
|
|
const pipeline = device.createRenderPipeline({
|
|
layout: 'auto',
|
|
vertex: {
|
|
module: shaderModule,
|
|
entryPoint: 'vertexMain'
|
|
},
|
|
fragment: {
|
|
module: shaderModule,
|
|
entryPoint: 'fragmentMain',
|
|
targets: [{ format }]
|
|
},
|
|
primitive: {
|
|
topology: 'triangle-list'
|
|
}
|
|
});
|
|
|
|
// Render Loop
|
|
function frame() {
|
|
const commandEncoder = device.createCommandEncoder();
|
|
|
|
const renderPass = commandEncoder.beginRenderPass({
|
|
colorAttachments: [{
|
|
view: context.getCurrentTexture().createView(),
|
|
clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
|
|
loadOp: 'clear',
|
|
storeOp: 'store'
|
|
}]
|
|
});
|
|
|
|
renderPass.setPipeline(pipeline);
|
|
renderPass.draw(3); // 3 vertices
|
|
renderPass.end();
|
|
|
|
device.queue.submit([commandEncoder.finish()]);
|
|
requestAnimationFrame(frame);
|
|
}
|
|
|
|
frame();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Compute Shaders
|
|
|
|
```typescript
|
|
// webgpu-compute.ts
|
|
|
|
// Particle Simulation mit Compute Shader
|
|
const computeShaderCode = /* wgsl */ `
|
|
struct Particle {
|
|
position: vec2f,
|
|
velocity: vec2f,
|
|
}
|
|
|
|
struct SimParams {
|
|
deltaTime: f32,
|
|
gravity: f32,
|
|
}
|
|
|
|
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
|
|
@group(0) @binding(1) var<uniform> params: SimParams;
|
|
|
|
@compute @workgroup_size(64)
|
|
fn main(@builtin(global_invocation_id) id: vec3u) {
|
|
let index = id.x;
|
|
if (index >= arrayLength(&particles)) {
|
|
return;
|
|
}
|
|
|
|
var particle = particles[index];
|
|
|
|
// Apply Gravity
|
|
particle.velocity.y -= params.gravity * params.deltaTime;
|
|
|
|
// Update Position
|
|
particle.position += particle.velocity * params.deltaTime;
|
|
|
|
// Bounce off boundaries
|
|
if (particle.position.y < -1.0) {
|
|
particle.position.y = -1.0;
|
|
particle.velocity.y *= -0.8; // Energy loss
|
|
}
|
|
|
|
if (abs(particle.position.x) > 1.0) {
|
|
particle.velocity.x *= -1.0;
|
|
}
|
|
|
|
particles[index] = particle;
|
|
}
|
|
`;
|
|
|
|
async function setupParticleSimulation(
|
|
device: GPUDevice,
|
|
particleCount: number
|
|
) {
|
|
// Initialize Particles
|
|
const particleData = new Float32Array(particleCount * 4);
|
|
for (let i = 0; i < particleCount; i++) {
|
|
particleData[i * 4 + 0] = (Math.random() - 0.5) * 2; // x
|
|
particleData[i * 4 + 1] = Math.random(); // y
|
|
particleData[i * 4 + 2] = (Math.random() - 0.5) * 0.1; // vx
|
|
particleData[i * 4 + 3] = 0; // vy
|
|
}
|
|
|
|
// Particle Buffer
|
|
const particleBuffer = device.createBuffer({
|
|
size: particleData.byteLength,
|
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
mappedAtCreation: true
|
|
});
|
|
new Float32Array(particleBuffer.getMappedRange()).set(particleData);
|
|
particleBuffer.unmap();
|
|
|
|
// Uniform Buffer
|
|
const uniformBuffer = device.createBuffer({
|
|
size: 8, // 2 x float32
|
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
});
|
|
|
|
// Compute Pipeline
|
|
const computeModule = device.createShaderModule({ code: computeShaderCode });
|
|
|
|
const computePipeline = device.createComputePipeline({
|
|
layout: 'auto',
|
|
compute: {
|
|
module: computeModule,
|
|
entryPoint: 'main'
|
|
}
|
|
});
|
|
|
|
// Bind Group
|
|
const bindGroup = device.createBindGroup({
|
|
layout: computePipeline.getBindGroupLayout(0),
|
|
entries: [
|
|
{ binding: 0, resource: { buffer: particleBuffer } },
|
|
{ binding: 1, resource: { buffer: uniformBuffer } }
|
|
]
|
|
});
|
|
|
|
return {
|
|
particleBuffer,
|
|
uniformBuffer,
|
|
computePipeline,
|
|
bindGroup,
|
|
particleCount
|
|
};
|
|
}
|
|
|
|
function runComputePass(
|
|
device: GPUDevice,
|
|
simulation: Awaited<ReturnType<typeof setupParticleSimulation>>,
|
|
deltaTime: number
|
|
) {
|
|
const { uniformBuffer, computePipeline, bindGroup, particleCount } = simulation;
|
|
|
|
// Update Uniforms
|
|
device.queue.writeBuffer(
|
|
uniformBuffer,
|
|
0,
|
|
new Float32Array([deltaTime, 9.81])
|
|
);
|
|
|
|
// Dispatch Compute
|
|
const commandEncoder = device.createCommandEncoder();
|
|
const computePass = commandEncoder.beginComputePass();
|
|
|
|
computePass.setPipeline(computePipeline);
|
|
computePass.setBindGroup(0, bindGroup);
|
|
computePass.dispatchWorkgroups(Math.ceil(particleCount / 64));
|
|
computePass.end();
|
|
|
|
device.queue.submit([commandEncoder.finish()]);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Three.js WebGPU Renderer
|
|
|
|
```typescript
|
|
// three-webgpu.ts
|
|
import * as THREE from 'three';
|
|
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
async function initThreeWebGPU() {
|
|
// WebGPU Renderer
|
|
const renderer = new WebGPURenderer({ antialias: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
document.body.appendChild(renderer.domElement);
|
|
|
|
// Initialize WebGPU
|
|
await renderer.init();
|
|
|
|
// Scene Setup
|
|
const scene = new THREE.Scene();
|
|
scene.background = new THREE.Color(0x111111);
|
|
|
|
// Camera
|
|
const camera = new THREE.PerspectiveCamera(
|
|
75,
|
|
window.innerWidth / window.innerHeight,
|
|
0.1,
|
|
1000
|
|
);
|
|
camera.position.z = 5;
|
|
|
|
// Controls
|
|
const controls = new OrbitControls(camera, renderer.domElement);
|
|
controls.enableDamping = true;
|
|
|
|
// Lights
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
|
scene.add(ambientLight);
|
|
|
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
|
|
directionalLight.position.set(10, 10, 10);
|
|
scene.add(directionalLight);
|
|
|
|
// Objects
|
|
const geometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16);
|
|
const material = new THREE.MeshStandardMaterial({
|
|
color: 0x00ff88,
|
|
metalness: 0.5,
|
|
roughness: 0.2
|
|
});
|
|
const mesh = new THREE.Mesh(geometry, material);
|
|
scene.add(mesh);
|
|
|
|
// Animation Loop
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
|
|
mesh.rotation.x += 0.01;
|
|
mesh.rotation.y += 0.01;
|
|
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
animate();
|
|
|
|
// Resize Handler
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## React Three Fiber mit WebGPU
|
|
|
|
```tsx
|
|
// components/WebGPUScene.tsx
|
|
'use client';
|
|
|
|
import { Canvas } from '@react-three/fiber';
|
|
import { OrbitControls, Environment } from '@react-three/drei';
|
|
import { Suspense } from 'react';
|
|
|
|
export function WebGPUScene() {
|
|
return (
|
|
<Canvas
|
|
gl={(canvas) => {
|
|
// WebGPU Renderer wird automatisch verwendet wenn verfügbar
|
|
// Fallback auf WebGL
|
|
return undefined;
|
|
}}
|
|
frameloop="demand"
|
|
>
|
|
<Suspense fallback={null}>
|
|
<Environment preset="city" />
|
|
<OrbitControls />
|
|
|
|
<mesh>
|
|
<torusKnotGeometry args={[1, 0.3, 100, 16]} />
|
|
<meshStandardMaterial color="#00ff88" metalness={0.5} roughness={0.2} />
|
|
</mesh>
|
|
</Suspense>
|
|
</Canvas>
|
|
);
|
|
}
|
|
|
|
// Feature Detection
|
|
export function useWebGPUSupport() {
|
|
const [supported, setSupported] = useState<boolean | null>(null);
|
|
|
|
useEffect(() => {
|
|
setSupported('gpu' in navigator);
|
|
}, []);
|
|
|
|
return supported;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Benchmarks
|
|
|
|
```typescript
|
|
// benchmarks/webgpu-vs-webgl.ts
|
|
|
|
interface BenchmarkResult {
|
|
name: string;
|
|
webgl: number;
|
|
webgpu: number;
|
|
speedup: number;
|
|
}
|
|
|
|
const benchmarks: BenchmarkResult[] = [
|
|
{
|
|
name: 'Draw Calls (1000 objects)',
|
|
webgl: 16.7, // ms
|
|
webgpu: 1.5, // ms
|
|
speedup: 11.1
|
|
},
|
|
{
|
|
name: 'Particle System (100k)',
|
|
webgl: 33.3, // ms (CPU-bound)
|
|
webgpu: 2.1, // ms (GPU Compute)
|
|
speedup: 15.9
|
|
},
|
|
{
|
|
name: 'Shadow Mapping',
|
|
webgl: 8.2, // ms
|
|
webgpu: 3.4, // ms
|
|
speedup: 2.4
|
|
},
|
|
{
|
|
name: 'Post-Processing (5 passes)',
|
|
webgl: 12.5, // ms
|
|
webgpu: 4.8, // ms
|
|
speedup: 2.6
|
|
},
|
|
{
|
|
name: 'ML Inference (ONNX)',
|
|
webgl: 45.0, // ms (limited)
|
|
webgpu: 8.0, // ms (Compute)
|
|
speedup: 5.6
|
|
}
|
|
];
|
|
|
|
// Render Bundles für maximale Performance
|
|
async function createRenderBundle(device: GPUDevice, pipeline: GPURenderPipeline) {
|
|
const encoder = device.createRenderBundleEncoder({
|
|
colorFormats: ['bgra8unorm']
|
|
});
|
|
|
|
encoder.setPipeline(pipeline);
|
|
|
|
// Pre-record alle Draw Calls
|
|
for (let i = 0; i < 1000; i++) {
|
|
encoder.draw(3);
|
|
}
|
|
|
|
return encoder.finish();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Feature Detection & Fallback
|
|
|
|
```typescript
|
|
// lib/gpu-detection.ts
|
|
|
|
interface GPUCapabilities {
|
|
webgpu: boolean;
|
|
webgl2: boolean;
|
|
webgl: boolean;
|
|
computeShaders: boolean;
|
|
maxTextureSize: number;
|
|
}
|
|
|
|
async function detectGPUCapabilities(): Promise<GPUCapabilities> {
|
|
const capabilities: GPUCapabilities = {
|
|
webgpu: false,
|
|
webgl2: false,
|
|
webgl: false,
|
|
computeShaders: false,
|
|
maxTextureSize: 0
|
|
};
|
|
|
|
// WebGPU Check
|
|
if ('gpu' in navigator) {
|
|
try {
|
|
const adapter = await navigator.gpu.requestAdapter();
|
|
if (adapter) {
|
|
capabilities.webgpu = true;
|
|
capabilities.computeShaders = true;
|
|
|
|
const limits = adapter.limits;
|
|
capabilities.maxTextureSize = limits.maxTextureDimension2D;
|
|
}
|
|
} catch {
|
|
// WebGPU not available
|
|
}
|
|
}
|
|
|
|
// WebGL2 Check
|
|
const canvas = document.createElement('canvas');
|
|
const gl2 = canvas.getContext('webgl2');
|
|
if (gl2) {
|
|
capabilities.webgl2 = true;
|
|
capabilities.maxTextureSize = Math.max(
|
|
capabilities.maxTextureSize,
|
|
gl2.getParameter(gl2.MAX_TEXTURE_SIZE)
|
|
);
|
|
}
|
|
|
|
// WebGL Check
|
|
const gl = canvas.getContext('webgl');
|
|
if (gl) {
|
|
capabilities.webgl = true;
|
|
}
|
|
|
|
return capabilities;
|
|
}
|
|
|
|
// Adaptive Rendering
|
|
async function createRenderer(canvas: HTMLCanvasElement) {
|
|
const caps = await detectGPUCapabilities();
|
|
|
|
if (caps.webgpu) {
|
|
console.log('Using WebGPU renderer');
|
|
return createWebGPURenderer(canvas);
|
|
}
|
|
|
|
if (caps.webgl2) {
|
|
console.log('Using WebGL2 renderer');
|
|
return createWebGL2Renderer(canvas);
|
|
}
|
|
|
|
if (caps.webgl) {
|
|
console.log('Using WebGL renderer (limited features)');
|
|
return createWebGLRenderer(canvas);
|
|
}
|
|
|
|
throw new Error('No GPU rendering available');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Fazit
|
|
|
|
WebGPU bietet:
|
|
|
|
1. **10x Performance**: Render Bundles, weniger Overhead
|
|
2. **Compute Shaders**: GPU für Berechnungen nutzen
|
|
3. **Moderne API**: Basiert auf Vulkan/Metal/D3D12
|
|
4. **Browser Support**: Alle Major Browser (2025/2026)
|
|
|
|
Die Zukunft der Browser-Grafik ist WebGPU.
|
|
|
|
---
|
|
|
|
## Bildprompts
|
|
|
|
1. "WebGPU particle simulation with 100k particles, compute shader visualization"
|
|
2. "Performance comparison chart WebGL vs WebGPU, bar graph"
|
|
3. "Modern GPU architecture diagram, WebGPU pipeline stages"
|
|
|
|
---
|
|
|
|
## Quellen
|
|
|
|
- [WebGPU MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
|
|
- [WebGPU Supported in All Browsers](https://web.dev/blog/webgpu-supported-major-browsers)
|
|
- [Three.js WebGPU Roadmap](https://threejsroadmap.com/blog/webgl-vs-webgpu-explained)
|
|
- [Chrome WebGPU Blog](https://developer.chrome.com/blog/webgpu-io2023)
|