WebGPU-Entwicklungsleitfaden
Dieser Leitfaden erklärt, wie Sie WebGPU in Ihrem Projekt zum Rendern von 3DGS-Szenen einsetzen, und welche Unterschiede zur WebGL-Integration bestehen.
Engine-Unterstützung
Die WebGPU-Pipeline unterstützt nur die Three.js-Engine. CesiumJS unterstützt kein WebGPU-Rendering.
Versionsanforderung
SDK-Version: SDK v0.6.4+
Einschränkungen beim Datenformat
Die WebGPU-Pipeline unterstützt derzeit nur das Format .lcc2. Das Format .lcc unterstützt noch kein WebGPU-Rendering; die Unterstützung folgt in einer späteren Version.
Wird ein .lcc-Datenpfad übergeben, sollten Sie auf den WebGL-Renderer zurückfallen. Der Beispielcode setzt diese Logik über den Parameter forceWebGL um:
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const renderer = await createRenderer(canvas, forceWebGL);
Einschränkungen bei WebXR
Derzeit unterstützt das SDK WebXR nur in der WebGL-Pipeline. Die WebGPU-Pipeline unterstützt kein WebXR.
Einschränkungen auf Mobilgeräten
WebGPU wird für Mobilgeräte nicht empfohlen. Die WebGL-Pipeline des SDK bietet auf Mobilgeräten derzeit die bessere Leistung.
Rendering-Pfade in Three.js
Three.js bietet drei Rendering-Pfade. Die Unterschiede zu kennen ist entscheidend für eine korrekte SDK-Integration.
Pfad 1: Klassischer WebGLRenderer (weit verbreitet)
WebGLRenderer ist seit den Anfängen von Three.js der zentrale Renderer und nach wie vor die am weitesten verbreitete Wahl. Er setzt direkt auf der WebGL-2-API auf, und sein Materialsystem verwendet GLSL-Shader.
Import:
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer();
Das SDK unterstützt diesen Pfad mit den Formaten .lcc und .lcc2.
Pfad 2: Neue Architektur WebGPURenderer + WebGPU-Backend
WebGPURenderer ist der Renderer der nächsten Generation von Three.js und basiert auf einer neuen, abstrahierten Backend-Architektur. Er führt ein knotenbasiertes Materialsystem (Node Material) und TSL (Three.js Shading Language) ein. In TSL geschriebene Shader werden je nach Backend automatisch zu WGSL oder GLSL kompiliert. Unterstützt der Browser WebGPU, verwendet WebGPURenderer das native WebGPU-Backend.
Import:
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas });
await renderer.init();
Das SDK unterstützt diesen Pfad, jedoch nur mit dem Format .lcc2.
Pfad 3: Neue Architektur WebGPURenderer + WebGL-2-Backend
WebGPURenderer enthält ein integriertes WebGL-2-Backend als Fallback. Unterstützt der Browser kein WebGPU, wechselt WebGPURenderer automatisch zum WebGL-2-Backend. Obwohl weiterhin WebGL 2 verwendet wird, sind Materialsystem und Shader-Pipeline identisch mit Pfad 2 (Node Material + TSL); das endgültige Kompilierungsziel ist GLSL.
Das SDK unterstützt diesen Pfad nicht.
Vergleich der Pfade
| Pfad | Renderer | Grafik-API | Material- / Shader-System | SDK-Unterstützung | Unterstützte Formate |
|---|---|---|---|---|---|
| 1. Klassisches WebGL | WebGLRenderer | WebGL 2 | GLSL (ShaderMaterial) | ✅ | .lcc .lcc2 |
| 2. Neue Architektur WebGPU | WebGPURenderer | WebGPU | Node Material / TSL / WGSL | ✅ | .lcc2 |
| 3. Neue Architektur WebGL-Fallback | WebGPURenderer | WebGL 2 (Backend-Fallback) | Node Material / TSL / GLSL | ❌ | - |
Unterschiede zwischen WebGPU- und WebGL-Integration
Im Vergleich zur WebGL-Integration beschränken sich die Änderungen für WebGPU auf die folgenden Bereiche. Der übrige Geschäftscode (Kamerasteuerung, Leistungsparameter, Render-Schleife usw.) bleibt identisch.
1. Zusätzliche Importe: three/webgpu und three/tsl
WebGL benötigt nur three. WebGPU benötigt zwei zusätzliche Module:
<!-- importmap -->
<script type="importmap">
{
"imports": {
"three": "./engine/three/three.module.js",
"three/webgpu": "./engine/three/three.webgpu.js",
"three/tsl": "./engine/three/three.tsl.js",
"three/addons/": "./engine/three/jsm/"
}
}
</script>
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
2. Asynchrone Erstellung des Renderers
Der WebGL-Renderer wird synchron erstellt. Der WebGPU-Renderer erfordert eine asynchrone Initialisierung:
// WebGL (synchron)
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false });
// WebGPU (asynchron)
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas, antialias: false });
await renderer.init();
Wir empfehlen die Hilfsfunktion createRenderer aus den SDK-Beispielen, die eine vollständige Fallback-Kette umsetzt:
- Der Browser unterstützt
navigator.gpunicht → Fallback aufWebGLRenderer - Kein GPU-Adapter verfügbar → Fallback auf
WebGLRenderer WebGPURendererfällt nach der Initialisierung unbemerkt auf das WebGL-2-Backend zurück (keinGPUDevice) → zerstören und Fallback aufWebGLRenderer- Alle Prüfungen bestanden → einen echten WebGPU-Renderer zurückgeben
import { createRenderer } from './engine/three/util.js';
const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;
3. sRGB-Ausgabe und Tone Mapping deaktivieren
Im WebGPU-Modus müssen Sie die standardmäßige sRGB-Ausgabekonvertierung und das Tone Mapping von Three.js deaktivieren. Die standardmäßige sRGB-Ausgabe fügt einen zusätzlichen Konvertierungsschritt linear → sRGB hinzu, der mit dem Premultiplied-Alpha-Blending von 3DGS kollidiert und zu Farbabweichungen führt:
if (isWebGPU) {
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
renderer.toneMapping = THREE.NoToneMapping;
}
Im WebGL-Modus ist dies nicht erforderlich.
4. Parameter von LCCRender.load
WebGPU benötigt die zusätzlichen Parameter renderLibWebgpu und renderLibTSL:
// WebGL
LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
canvas: renderer.domElement,
// ...
});
// WebGPU
LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
renderLibWebgpu: THREE_WEBGPU, // neu
renderLibTSL: THREE_TSL, // neu
canvas: renderer.domElement,
// ...
});
Konfiguration der GPU-Gerätelimits
Beim Erstellen eines WebGPURenderer empfiehlt es sich, die tatsächlichen Limits des GPU-Adapters abzufragen und als requiredLimits zu übergeben, um die Fähigkeiten des Geräts voll auszuschöpfen:
const adapter = await navigator.gpu.requestAdapter();
const requiredLimits = {
maxBufferSize: adapter.limits.maxBufferSize,
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
maxTextureDimension2D: adapter.limits.maxTextureDimension2D
};
const renderer = new THREE_WEBGPU.WebGPURenderer({
canvas,
antialias: false,
requiredLimits
});
await renderer.init();
Vollständiges WebGPU-Beispiel
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LCC Three.js WebGPU Demo</title>
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "./engine/three/three.module.js",
"three/webgpu": "./engine/three/three.webgpu.js",
"three/tsl": "./engine/three/three.tsl.js",
"three/addons/": "./engine/three/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
import { createRenderer } from './engine/three/util.js';
import { LCCRender } from '../sdk/lcc-sdk-three.js';
const dataPath = 'https://your-server.com/assets/scene/meta.lcc2';
// WebGPU unterstützt nur .lcc2; für .lcc-Daten WebGL erzwingen
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const scene = new THREE.Scene();
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// Renderer mit automatischem Fallback WebGPU → WebGL erstellen
const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000);
// sRGB-Ausgabe für WebGPU deaktivieren, um Farbraumkonflikte mit 3DGS zu vermeiden
if (isWebGPU) {
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
renderer.toneMapping = THREE.NoToneMapping;
}
const camera = new THREE.PerspectiveCamera(
45, window.innerWidth / window.innerHeight, 0.1, 150000
);
camera.position.set(0, 2, 0);
// 3DGS-Szene laden
const lccObj = LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
renderLibWebgpu: THREE_WEBGPU,
renderLibTSL: THREE_TSL,
canvas: renderer.domElement,
useEnv: true,
useIndexDB: true,
useLoadingEffect: true,
appKey: null // durch Ihren eigenen Schlüssel ersetzen
},
(mesh) => { console.log('Loaded', mesh); },
(percent) => { console.log('Loading ' + (percent * 100).toFixed(1) + '%'); },
() => { console.log('Loading failed'); }
);
// Leistungsparameter konfigurieren
lccObj.setStartLod(0);
lccObj.setMaxSplats(8000000);
lccObj.setMaxNodeSplats(4000000);
lccObj.setMaxDistance(200);
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
renderer.setAnimationLoop(() => {
LCCRender.update();
renderer.render(scene, camera);
});
</script>
</body>
</html>